Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Proaktive Integritätsprüfungen für SQL Server, Teil 3:Instanz- und Datenbankeinstellungen

Unsere Diskussion über proaktive Aufgaben, die Ihre Datenbank gesund halten, wird in diesem Beitrag fortgesetzt, während wir uns mit Server- und Datenbankoptionen befassen. Sie denken vielleicht schon, dass dies ein kurzer Beitrag sein wird – wer ändert so oft Server- oder Datenbankoptionen? Sie wären überrascht, besonders wenn Sie viele Leute haben, die Zugriff auf den SQL Server haben. Server- und Datenbankoptionen sollten sich selten ändern – zum größten Teil werden diese bei der Installation festgelegt und in Ruhe gelassen. Aber hin und wieder gibt es gute Gründe für eine Änderung – sei es aus Performancegründen, aufgrund einer Änderung des Anwendungscodes oder weil beim ersten Mal etwas falsch eingestellt wurde. Testen Sie diese Änderungen zuerst und erfassen Sie geeignete Metriken vor und nach der Änderung. Es scheint ziemlich einfach und offensichtlich, oder? Sie könnten das denken, aber wenn Sie keinen Change-Management-Prozess haben, der strikt befolgt wird, ist es nicht so.

In den meisten Umgebungen hat mehr als eine Person Zugriff auf SQL Server, und mehr als eine Person verfügt über die Berechtigungen, die zum Ändern von Server- oder Datenbankoptionen erforderlich sind. Wenn die falsche Einstellung geändert wird, kann die Leistung erheblich beeinträchtigt werden. (Haben Sie jemals versehentlich die Einstellung für den maximalen Arbeitsspeicher auf einen Wert in GB anstelle von MB gesetzt? Falls Sie sich fragen, dass 128 MB nicht genug Arbeitsspeicher für den Start einer SQL Server-Instanz benötigt werden. Sehen Sie sich den Beitrag von Ted Krueger an, wie Sie dies beheben können , sollten Sie jemals diesen Fehler machen.) Andere Änderungen können kleinere Probleme verursachen, die immer noch lästig und manchmal schwer aufzuspüren sind (das Deaktivieren von Statistiken zum automatischen Erstellen ist ein gutes Beispiel). Sie denken vielleicht, dass diese Änderungen gut kommuniziert werden (manchmal ist man so damit beschäftigt, Feuer zu löschen, dass man es vergisst) oder leicht zu bemerken (nicht immer). Um dies zu vermeiden, verfolgen wir die Einstellungen und stellen dann bei unseren regelmäßigen Überprüfungen (oder bei der Fehlerbehebung) sicher, dass sich nichts geändert hat.

Erfassen der Daten

Im Gegensatz zum vorherigen Beitrag zu Wartungsaufgaben, bei dem wir uns auf msdb verlassen haben, um die uns wichtigen Daten zu speichern, müssen wir die Datenerfassung für Instanzen und Datenbankeinstellungen einrichten. Wir erstellen täglich Snapshots von sys.configurations und sys.database_info in Tabellen in unserer Baselines-Datenbank und verwenden dann Abfragen, um festzustellen, ob und wann sich etwas geändert hat.

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

Das Skript zum Erstellen der Tabelle SQLskills_DBData ist mit SQL Server 2014 kompatibel. Bei früheren Versionen müssen Sie möglicherweise die Basistabelle und die Snapshot-Abfrage ändern (siehe nächster Codesatz).

Sobald Sie die Tabellen erstellt haben, erstellen Sie einen Job, der die folgenden zwei Abfragen täglich ausführt. Auch hier würden wir nicht erwarten, dass sich diese Optionen mehr als einmal am Tag ändern, und obwohl wir hoffen, dass niemand eine Einstellung ändert und sie dann wieder zurücksetzt (daher würde sie in einer Aufnahme nicht angezeigt), ist dies immer eine Möglichkeit . Wenn Sie feststellen, dass diese Datenerfassung nicht Ihren Anforderungen entspricht, weil sich die Einstellungen häufig oder vorübergehend ändern, möchten Sie möglicherweise einen Trigger implementieren oder Auditing verwenden.

Um Serveroptionen über (sp_configure) zu bearbeiten, benötigt eine Anmeldung die Berechtigung ALTER SETTINGS auf Serverebene, die enthalten ist, wenn Sie Mitglied der Rollen sysadmin oder serveradmin sind. Um die meisten Datenbankeinstellungen (ALTER DATABASE SET) zu bearbeiten, benötigen Sie die ALTER-Berechtigung in der Datenbank, obwohl einige Optionen zusätzliche Rechte erfordern, wie z. B. CONTROL SERVER oder die Option ALTER ANY DATABASE auf Serverebene.

/* 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

Auf Änderungen prüfen

Nun, da wir diese Informationen erfassen, wie finden wir Änderungen? Da wir wissen, dass mehrere Einstellungen und an unterschiedlichen Daten geändert werden können, benötigen wir eine Methode, die jede Zeile betrachtet. Das ist nicht schwer, generiert aber nicht den schönsten Code. Für Serveroptionen ist es nicht so schlimm:

;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

Geänderte Instanzeinstellungen

Bei Datenbankoptionen befindet sich die Abfrage in einer gespeicherten Prozedur (weil sie so unhandlich war), die Sie hier herunterladen können. So führen Sie die gespeicherte Prozedur aus:

EXEC dbo.usp_FindDBSettingChanges

Die Ausgabe listet die Datenbank und die geänderte Einstellung sowie das Datum auf:

Geänderte Datenbankeinstellungen

Sie können diese Abfragen ausführen, wenn Leistungsprobleme auftreten, um schnell zu überprüfen, ob sich Einstellungen geändert haben, oder Sie könnten etwas proaktiver sein und sie regelmäßig in einem geplanten Job ausführen, der Sie benachrichtigt, wenn sich etwas geändert hat. Ich habe den T-SQL-Code nicht eingefügt, um eine E-Mail mit Datenbank-E-Mail zu senden, wenn es eine Änderung gibt, aber das wird basierend auf dem hier bereitgestellten Code nicht schwer sein.

Leistungsratgeber verwenden

SQL Sentry Performance Advisor verfolgt diese Informationen nicht standardmäßig, aber Sie können die Informationen trotzdem in einer Datenbank erfassen, dann von PA überprüfen lassen, ob sich Einstellungen geändert haben, und Sie benachrichtigen, wenn dies der Fall ist. Um dies einzurichten, erstellen Sie die Tabellen SQLskills_ConfigData und SQLskillsDBData und richten Sie den geplanten Auftrag so ein, dass er regelmäßig in diese Tabellen eingefügt wird. Richten Sie innerhalb des SQL Sentry-Clients eine benutzerdefinierte Bedingung ein, wie wir es in einem früheren Beitrag dieser Reihe, Proactive SQL Server Health Checks, Part 1:Disk Space post, getan haben.

Innerhalb der benutzerdefinierten Bedingung haben Sie zwei Optionen. Erstens könnten Sie einfach den bereitgestellten Code ausführen, der historische Daten überprüft, um festzustellen, ob sich etwas geändert hat (und dann eine Benachrichtigung senden, wenn dies der Fall ist). Das Überprüfen historischer Daten auf Änderungen ist etwas, das Sie täglich ausführen würden, wie Sie es bei einem Agentenjob tun würden. Alternativ könnten Sie proaktiver vorgehen und aktuelle, laufende Werte häufiger mit den neuesten Daten vergleichen, z. einmal pro Stunde, um nach Änderungen zu suchen. Beispielcode zum Vergleichen der aktuellen Einstellungen für die Instanz mit der neuesten Erfassung:

;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

Zusammenfassung

Das Überprüfen von Instanz- und Datenbankoptionen ist einfach und offensichtlich, und in einigen Situationen können Ihnen diese historischen Informationen viel Zeit bei der Fehlerbehebung sparen. Wenn Sie diese Informationen nirgendwo erfassen, ermutige ich Sie, damit zu beginnen; Es ist immer besser, proaktiv nach Problemen zu suchen, als zu reagieren, wenn Sie in der Brandbekämpfung sind und möglicherweise gestresst sind, weil Sie sich nicht sicher sind, was ein Problem in Ihrer Produktionsumgebung verursacht.