Notre discussion sur les tâches proactives qui maintiennent votre base de données en bonne santé se poursuit dans cet article alors que nous abordons les options de serveur et de base de données. Vous pensez peut-être déjà que ce sera un article rapide - qui change si souvent les options de serveur ou de base de données ? Vous seriez surpris, surtout si vous avez beaucoup de personnes qui ont accès à SQL Server. Les options de serveur et de base de données devraient changer rarement - pour la plupart, elles sont définies lors de l'installation et laissées seules. Mais de temps en temps, il y a de bonnes raisons de faire un changement - que ce soit lié aux performances, en raison d'un changement dans le code de l'application, ou peut-être parce que quelque chose a été mal défini la première fois. Testez d'abord ces modifications et capturez les métriques appropriées avant et après la modification. Cela semble assez simple et évident, non? Vous pensez peut-être que oui, mais si vous n'avez pas mis en place un processus de gestion des changements strictement suivi, ce n'est pas le cas.
Dans la majorité des environnements, plusieurs personnes ont accès à SQL Server et plusieurs personnes disposent des privilèges nécessaires pour modifier les options du serveur ou de la base de données. Si le mauvais paramètre est modifié, l'impact sur les performances peut être significatif. (Avez-vous déjà défini par inadvertance le paramètre de mémoire maximale sur une valeur en Go au lieu de Mo ? Au cas où vous vous poseriez la question, 128 Mo ne suffisent pas pour démarrer une instance SQL Server. Consultez le post de Ted Krueger sur la façon de résoudre ce problème , si jamais vous commettiez cette erreur.) D'autres modifications peuvent créer des problèmes plus petits qui sont encore gênants et parfois difficiles à détecter (la désactivation de la création automatique de statistiques en est un bon exemple). Vous pensez peut-être que ces changements seraient bien communiqués (parfois vous êtes tellement occupé à éteindre des incendies que vous oubliez) ou faciles à remarquer (pas toujours). Pour éviter cela, nous suivons les paramètres, puis, lors de nos vérifications régulières (ou lors du dépannage d'un problème), nous vérifions que rien n'a changé.
Capturer les données
Contrairement au post précédent sur les tâches de maintenance, où nous comptions sur msdb pour conserver les données qui nous intéressaient, nous devons configurer la capture de données pour les paramètres d'instance et de base de données. Nous créerons quotidiennement des instantanés de sys.configurations et sys.database_info dans des tables de notre base de données de référence, puis utiliserons des requêtes pour voir si quelque chose a changé, et quand.
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
Le script pour créer la table SQLskills_DBData est compatible avec SQL Server 2014. Pour les versions antérieures, vous devrez peut-être modifier la table de base et la requête d'instantané (voir l'ensemble de code suivant).
Une fois les tables créées, créez une tâche qui exécutera quotidiennement les deux requêtes suivantes. Encore une fois, nous ne nous attendrions pas à ce que ces options changent plus d'une fois par jour, et même si nous espérons que personne ne modifiera un paramètre, puis le modifiera (il n'apparaîtra donc pas dans une capture), c'est toujours une possibilité . Si vous trouvez que cette capture de données ne répond pas à vos besoins, car les paramètres changent fréquemment ou temporairement, vous pouvez implémenter un déclencheur ou utiliser l'audit.
Pour modifier les options du serveur via (sp_configure), une connexion nécessite l'autorisation au niveau du serveur ALTER SETTINGS, qui est incluse si vous êtes membre des rôles sysadmin ou serveradmin. Pour modifier la plupart des paramètres de base de données (ALTER DATABASE SET), vous avez besoin de l'autorisation ALTER dans la base de données, bien que certaines options nécessitent des droits supplémentaires, tels que CONTROL SERVER ou l'option au niveau du serveur 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
Vérification des modifications
Maintenant que nous capturons ces informations, comment trouvons-nous les changements ? Sachant que plusieurs paramètres peuvent être modifiés et à des dates différentes, nous avons besoin d'une méthode qui examine chaque ligne. Ce n'est pas difficile à faire, mais cela ne génère pas le plus beau code. Pour les options du serveur, c'est pas trop mal :
;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
Paramètres d'instance modifiés
Pour les options de base de données, la requête est dans une procédure stockée (parce qu'elle était si lourde), que vous pouvez télécharger ici. Pour exécuter la procédure stockée :
EXEC dbo.usp_FindDBSettingChanges
La sortie répertorie la base de données et le paramètre qui a changé, ainsi que la date :
Paramètres de base de données modifiés
Vous pouvez exécuter ces requêtes lorsque des problèmes de performances surviennent, pour vérifier rapidement si des paramètres ont changé, ou vous pouvez être un peu plus proactif et les exécuter régulièrement dans une tâche planifiée qui vous avertit si quelque chose a changé. Je n'ai pas inclus le code T-SQL pour envoyer un e-mail à l'aide de la messagerie de la base de données s'il y a un changement, mais cela ne sera pas difficile à faire sur la base du code fourni ici.
Utilisation de l'assistant de performances
SQL Sentry Performance Advisor ne suit pas ces informations par défaut, mais vous pouvez toujours capturer les informations dans une base de données, puis demander à PA de vérifier si des paramètres ont été modifiés et de vous avertir si tel est le cas. Pour le configurer, créez les tables SQLskills_ConfigData et SQLskillsDBData et configurez la tâche planifiée à insérer dans ces tables de manière régulière. Dans le client SQL Sentry, configurez une condition personnalisée, comme nous l'avons fait dans un article précédent de cette série, Proactive SQL Server Health Checks, Part 1:Disk Space post.
Dans la condition personnalisée, vous avez deux options. Tout d'abord, vous pouvez simplement exécuter le code fourni qui vérifie les données historiques pour voir si quelque chose a changé (et ensuite envoyer une notification si c'est le cas). La vérification des modifications des données historiques est quelque chose que vous exécuteriez quotidiennement, comme vous le feriez avec un travail d'agent. Alternativement, vous pouvez être plus proactif et comparer plus fréquemment les valeurs courantes actuelles avec les données les plus récentes, par ex. une fois par heure, pour rechercher les changements. Exemple de code pour vérifier les paramètres actuels de l'instance par rapport à la capture la plus récente :
;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
Résumé
La vérification des options d'instance et de base de données est simple et évidente, et dans certaines situations, ces informations historiques peuvent vous faire gagner un temps considérable lors du dépannage. Si vous ne capturez ces informations nulle part, je vous encourage à commencer ; il est toujours préférable de rechercher les problèmes de manière proactive plutôt que de réagir lorsque vous luttez contre les incendies et que vous êtes potentiellement stressé, incertain de ce qui cause un problème dans votre environnement de production.