La nostra discussione sulle attività proattive che mantengono sano il tuo database continua in questo post mentre affrontiamo le opzioni di server e database. Potresti già pensare che questo sarà un post veloce:chi cambia le opzioni del server o del database così spesso? Saresti sorpreso, soprattutto se hai molte persone che hanno accesso a SQL Server. Le opzioni del server e del database dovrebbero cambiare di rado, per la maggior parte queste sono impostate al momento dell'installazione e lasciate sole. Ma ogni tanto c'è una buona ragione per apportare una modifica, che sia legata alle prestazioni, a causa di una modifica nel codice dell'applicazione o forse perché qualcosa è stato impostato in modo errato la prima volta. Testare prima queste modifiche e acquisire le metriche appropriate prima e dopo la modifica. Sembra abbastanza semplice e ovvio, giusto? Potresti pensarlo, ma se non disponi di un processo di gestione delle modifiche rigorosamente seguito, non lo è.
Nella maggior parte degli ambienti, più di una persona ha accesso a SQL Server e più di una persona dispone dei privilegi necessari per modificare le opzioni del server o del database. Se si modifica l'impostazione errata, l'impatto sulle prestazioni può essere significativo. (Hai mai impostato inavvertitamente l'impostazione della memoria massima su un valore in GB anziché in MB? Nel caso te lo stia chiedendo, 128 MB non sono sufficienti per l'avvio di un'istanza di SQL Server. Dai un'occhiata al post di Ted Krueger su come risolvere questo problema , se dovessi mai commettere quell'errore.) Altre modifiche possono creare problemi più piccoli che sono ancora fastidiosi e talvolta difficili da rintracciare (la disabilitazione della creazione automatica delle statistiche è un buon esempio). Potresti pensare che questi cambiamenti sarebbero ben comunicati (a volte sei così impegnato a spegnere incendi che dimentichi) o facili da notare (non sempre). Per evitare ciò, teniamo traccia delle impostazioni e quindi, quando eseguiamo i nostri controlli regolari (o durante la risoluzione di un problema), verifichiamo che non sia cambiato nulla.
Acquisizione dei dati
A differenza del post precedente sulle attività di manutenzione, in cui facevamo affidamento su msdb per conservare i dati a cui tenevamo, dobbiamo configurare l'acquisizione dei dati, ad esempio, e le impostazioni del database. Faremo snapshot di sys.configurations e sys.database_info quotidianamente nelle tabelle nel nostro database Baselines, quindi utilizzeremo le query per vedere se qualcosa è cambiato e quando.
USE [Baselines]; GO IF OBJECT_ID(N'dbo.SQLskills_ConfigData', N'U') IS NULL BEGIN CREATE TABLE [dbo].[SQLskills_ConfigData] ( [ConfigurationID] [int] NOT NULL , [Name] [nvarchar](35) NOT NULL , [Value] [sql_variant] NULL , [ValueInUse] [sql_variant] NULL , [CaptureDate] [datetime] NOT NULL DEFAULT SYSDATETIME() ) ON [PRIMARY]; GO CREATE CLUSTERED INDEX [CI_SQLskills_ConfigData] ON [dbo].[SQLskills_ConfigData] ([CaptureDate],[ConfigurationID]); GO IF OBJECT_ID(N'dbo.SQLskills_DBData', N'U') IS NULL BEGIN CREATE TABLE [dbo].[SQLskills_DBData] ( [name] [sysname] NOT NULL, [database_id] [int] NOT NULL, [source_database_id] [int] NULL, [owner_sid] [varbinary](85) NULL, [create_date] [datetime] NOT NULL, [compatibility_level] [tinyint] NOT NULL, [collation_name] [sysname] NULL, [user_access] [tinyint] NULL, [user_access_desc] [nvarchar](60) NULL, [is_read_only] [bit] NULL, [is_auto_close_on] [bit] NOT NULL, [is_auto_shrink_on] [bit] NULL, [state] [tinyint] NULL, [state_desc] [nvarchar](60) NULL, [is_in_standby] [bit] NULL, [is_cleanly_shutdown] [bit] NULL, [is_supplemental_logging_enabled] [bit] NULL, [snapshot_isolation_state] [tinyint] NULL, [snapshot_isolation_state_desc] [nvarchar](60) NULL, [is_read_committed_snapshot_on] [bit] NULL, [recovery_model] [tinyint] NULL, [recovery_model_desc] [nvarchar](60) NULL, [page_verify_option] [tinyint] NULL, [page_verify_option_desc] [nvarchar](60) NULL, [is_auto_create_stats_on] [bit] NULL, [is_auto_update_stats_on] [bit] NULL, [is_auto_update_stats_async_on] [bit] NULL, [is_ansi_null_default_on] [bit] NULL, [is_ansi_nulls_on] [bit] NULL, [is_ansi_padding_on] [bit] NULL, [is_ansi_warnings_on] [bit] NULL, [is_arithabort_on] [bit] NULL, [is_concat_null_yields_null_on] [bit] NULL, [is_numeric_roundabort_on] [bit] NULL, [is_quoted_identifier_on] [bit] NULL, [is_recursive_triggers_on] [bit] NULL, [is_cursor_close_on_commit_on] [bit] NULL, [is_local_cursor_default] [bit] NULL, [is_fulltext_enabled] [bit] NULL, [is_trustworthy_on] [bit] NULL, [is_db_chaining_on] [bit] NULL, [is_parameterization_forced] [bit] NULL, [is_master_key_encrypted_by_server] [bit] NOT NULL, [is_published] [bit] NOT NULL, [is_subscribed] [bit] NOT NULL, [is_merge_published] [bit] NOT NULL, [is_distributor] [bit] NOT NULL, [is_sync_with_backup] [bit] NOT NULL, [service_broker_guid] [uniqueidentifier] NOT NULL, [is_broker_enabled] [bit] NOT NULL, [log_reuse_wait] [tinyint] NULL, [log_reuse_wait_desc] [nvarchar](60) NULL, [is_date_correlation_on] [bit] NOT NULL, [is_cdc_enabled] [bit] NOT NULL, [is_encrypted] [bit] NULL, [is_honor_broker_priority_on] [bit] NULL, [replica_id] [uniqueidentifier] NULL, [group_database_id] [uniqueidentifier] NULL, [default_language_lcid] [smallint] NULL, [default_language_name] [nvarchar](128) NULL, [default_fulltext_language_lcid] [int] NULL, [default_fulltext_language_name] [nvarchar](128) NULL, [is_nested_triggers_on] [bit] NULL, [is_transform_noise_words_on] [bit] NULL, [two_digit_year_cutoff] [smallint] NULL, [containment] [tinyint] NULL, [containment_desc] [nvarchar](60) NULL, [target_recovery_time_in_seconds] [int] NULL, [CaptureDate] [datetime] NOT NULL DEFAULT SYSDATETIME() ) ON [PRIMARY]; GO CREATE CLUSTERED INDEX [CI_SQLskills_DBData] ON [dbo].[SQLskills_DBData] ([CaptureDate],[database_id]); GO
Lo script per creare la tabella SQLskills_DBData è compatibile con SQL Server 2014. Per le versioni precedenti, potrebbe essere necessario modificare la tabella di base e la query snapshot (vedi set di codice successivo).
Una volta create le tabelle, crea un lavoro che eseguirà le seguenti due query ogni giorno. Ancora una volta, non ci aspetteremmo che queste opzioni cambino più di una volta al giorno, e mentre speriamo che nessuno modifichi un'impostazione, quindi la modifichi nuovamente (quindi non verrebbe visualizzata in una cattura), questa è sempre una possibilità . Se ritieni che questa acquisizione di dati non soddisfi le tue esigenze, poiché le impostazioni cambiano frequentemente o temporaneamente, potresti voler implementare un trigger o utilizzare il controllo.
Per modificare le opzioni del server tramite (sp_configure), un accesso richiede l'autorizzazione a livello di server ALTER SETTINGS, inclusa se sei un membro dei ruoli sysadmin o serveradmin. Per modificare la maggior parte delle impostazioni del database (ALTER DATABASE SET), è necessaria l'autorizzazione ALTER nel database, sebbene alcune opzioni richiedano diritti aggiuntivi, come CONTROL SERVER o l'opzione a livello di server ALTER ANY DATABASE.
/* Statements to use in scheduled job */ INSERT INTO [dbo].[SQLskills_ConfigData] ( [ConfigurationID] , [Name] , [Value] , [ValueInUse] ) SELECT [configuration_id] , [name] , [value] , [value_in_use] FROM [sys].[configurations]; GO INSERT INTO [dbo].[SQLskills_DBData] ( [name], [database_id], [source_database_id], [owner_sid], [create_date], [compatibility_level], [collation_name], [user_access], [user_access_desc], [is_read_only], [is_auto_close_on], [is_auto_shrink_on], [state], [state_desc], [is_in_standby], [is_cleanly_shutdown], [is_supplemental_logging_enabled], [snapshot_isolation_state], [snapshot_isolation_state_desc], [is_read_committed_snapshot_on], [recovery_model], [recovery_model_desc], [page_verify_option], [page_verify_option_desc], [is_auto_create_stats_on], [is_auto_update_stats_on], [is_auto_update_stats_async_on], [is_ansi_null_default_on], [is_ansi_nulls_on], [is_ansi_padding_on], [is_ansi_warnings_on], [is_arithabort_on], [is_concat_null_yields_null_on], [is_numeric_roundabort_on], [is_quoted_identifier_on], [is_recursive_triggers_on], [is_cursor_close_on_commit_on], [is_local_cursor_default], [is_fulltext_enabled], [is_trustworthy_on], [is_db_chaining_on], [is_parameterization_forced], [is_master_key_encrypted_by_server], [is_published], [is_subscribed], [is_merge_published], [is_distributor], [is_sync_with_backup], [service_broker_guid], [is_broker_enabled], [log_reuse_wait], [log_reuse_wait_desc], [is_date_correlation_on], [is_cdc_enabled], [is_encrypted], [is_honor_broker_priority_on], [replica_id], [group_database_id], [default_language_lcid], [default_language_name], [default_fulltext_language_lcid], [default_fulltext_language_name], [is_nested_triggers_on], [is_transform_noise_words_on], [two_digit_year_cutoff], [containment], [containment_desc], [target_recovery_time_in_seconds] ) SELECT [name], [database_id], [source_database_id], [owner_sid], [create_date], [compatibility_level], [collation_name], [user_access], [user_access_desc], [is_read_only], [is_auto_close_on], [is_auto_shrink_on], [state], [state_desc], [is_in_standby], [is_cleanly_shutdown], [is_supplemental_logging_enabled], [snapshot_isolation_state], [snapshot_isolation_state_desc], [is_read_committed_snapshot_on], [recovery_model], [recovery_model_desc], [page_verify_option], [page_verify_option_desc], [is_auto_create_stats_on], [is_auto_update_stats_on], [is_auto_update_stats_async_on], [is_ansi_null_default_on], [is_ansi_nulls_on], [is_ansi_padding_on], [is_ansi_warnings_on], [is_arithabort_on], [is_concat_null_yields_null_on], [is_numeric_roundabort_on], [is_quoted_identifier_on], [is_recursive_triggers_on], [is_cursor_close_on_commit_on], [is_local_cursor_default], [is_fulltext_enabled], [is_trustworthy_on], [is_db_chaining_on], [is_parameterization_forced], [is_master_key_encrypted_by_server], [is_published], [is_subscribed], [is_merge_published], [is_distributor], [is_sync_with_backup], [service_broker_guid], [is_broker_enabled], [log_reuse_wait], [log_reuse_wait_desc], [is_date_correlation_on], [is_cdc_enabled], [is_encrypted], [is_honor_broker_priority_on], [replica_id], [group_database_id], [default_language_lcid], [default_language_name], [default_fulltext_language_lcid], [default_fulltext_language_name], [is_nested_triggers_on], [is_transform_noise_words_on], [two_digit_year_cutoff], [containment], [containment_desc], [target_recovery_time_in_seconds] FROM [sys].[databases]; GO
Verifica modifiche
Ora che stiamo acquisendo queste informazioni, come troviamo le modifiche? Sapendo che potrebbero essere modificate più impostazioni e in date diverse, abbiamo bisogno di un metodo che esamini ogni riga. Questo non è difficile da fare, ma non genera il codice più carino. Per le opzioni del server, non è male:
;WITH [f] AS ( SELECT ROW_NUMBER() OVER (PARTITION BY [ConfigurationID] ORDER BY [CaptureDate] ASC) AS [RowNumber], [ConfigurationID] AS [ConfigurationID], [Name] AS [Name], [Value] AS [Value], [ValueInUse] AS [ValueInUse], [CaptureDate] AS [CaptureDate] FROM [Baselines].[dbo].[ConfigData] ) SELECT [f].[Name] AS [Setting], [f].[CaptureDate] AS [Date], [f].[Value] AS [Previous Value], [f].[ValueInUse] AS [Previous Value In Use], [n].[CaptureDate] AS [Date Changed], [n].[Value] AS [New Value], [n].[ValueInUse] AS [New Value In Use] FROM [f] LEFT OUTER JOIN [f] AS [n] ON [f].[ConfigurationID] = [n].[ConfigurationID] AND [f].[RowNumber] + 1 = [n].[RowNumber] WHERE ([f].[Value] <> [n].[Value] OR [f].[ValueInUse] <> [n].[ValueInUse]); GO
Impostazioni istanza modificate
Per le opzioni del database, la query è in una procedura memorizzata (perché era così ingombrante), che puoi scaricare qui. Per eseguire la procedura memorizzata:
EXEC dbo.usp_FindDBSettingChanges
L'output elencherà il database e l'impostazione modificata, nonché la data:
Impostazioni database modificate
Puoi eseguire queste query quando si verificano problemi di prestazioni, per verificare rapidamente se alcune impostazioni sono state modificate, oppure potresti essere un po' più proattivo ed eseguirle regolarmente in un processo pianificato che ti avvisa se qualcosa è cambiato. Non ho incluso il codice T-SQL per inviare un'e-mail utilizzando la posta del database in caso di modifiche, ma non sarà difficile farlo in base al codice fornito qui.
Utilizzo di Performance Advisor
SQL Sentry Performance Advisor non tiene traccia di queste informazioni per impostazione predefinita, ma puoi comunque acquisire le informazioni in un database, quindi fare in modo che PA verifichi se alcune impostazioni sono state modificate e avvisarti se lo sono. Per configurarlo, crea le tabelle SQLskills_ConfigData e SQLskillsDBData e imposta il lavoro pianificato da inserire in tali tabelle su base regolare. All'interno del client SQL Sentry, imposta una condizione personalizzata, come abbiamo fatto in un post precedente di questa serie, Controlli di integrità proattivi di SQL Server, Parte 1:Post di spazio su disco.
All'interno della condizione personalizzata, hai due opzioni. Innanzitutto, puoi semplicemente eseguire il codice fornito che controlla i dati storici per vedere se qualcosa è cambiato (e quindi inviare una notifica in tal caso). Il controllo dei dati storici per le modifiche è qualcosa che eseguiresti quotidianamente, come faresti con un lavoro agente. In alternativa, potresti essere più proattivo e confrontare i valori correnti e correnti con i dati più recenti su base più frequente, ad es. una volta all'ora, per cercare i cambiamenti. Codice di esempio per verificare le impostazioni correnti dell'istanza rispetto all'acquisizione più recente:
;WITH [lc] AS ( SELECT ROW_NUMBER() OVER (PARTITION BY [ConfigurationID] ORDER BY [CaptureDate] ASC) AS [RowNumber], [ConfigurationID] AS [ConfigurationID], [Name] AS [Name], [Value] AS [Value], [ValueInUse] AS [ValueInUse], [CaptureDate] AS [CaptureDate] FROM [Baselines].[ConfigData] WHERE [CaptureDate] = (SELECT MAX([CaptureDate]) FROM [Baselines].[ConfigData]) ) SELECT [lc].[Name] AS [Setting], [lc].[CaptureDate] AS [Date], [lc].[Value] AS [Last Captured Value], [lc].[ValueInUse] AS [Last Captured Value In Use], CURRENT_TIMESTAMP AS [Current Time], [c].[Value] AS [Current Value], [c].[value_in_use] AS [Current Value In Use] FROM [sys].[configurations] AS [c] LEFT OUTER JOIN [lc] ON [lc].[ConfigurationID] = [c].[configuration_id] WHERE ([lc].[Value] <> [c].[Value] OR [lc].[ValueInUse] <> [c].[value_in_use]); GO
Riepilogo
Il controllo delle opzioni dell'istanza e del database è semplice ed ovvio e in alcune situazioni queste informazioni storiche possono farti risparmiare molto tempo durante la risoluzione dei problemi. Se non stai acquisendo queste informazioni da nessuna parte, ti incoraggio a iniziare; è sempre meglio cercare i problemi in modo proattivo piuttosto che reagire quando stai combattendo un incendio e sei potenzialmente stressato, incerto su cosa stia causando un problema nel tuo ambiente di produzione.