sql >> Databasteknik >  >> RDS >> Sqlserver

Proaktiva SQL Server Health Checks, Del 3:Inställningar för instans och databas

Vår diskussion om proaktiva uppgifter som håller din databas frisk fortsätter i det här inlägget när vi tar itu med server- och databasalternativ. Du kanske redan tror att det här kommer att bli ett snabbt inlägg – vem byter server- eller databasalternativ så ofta? Du skulle bli förvånad, särskilt om du har många människor som har tillgång till SQL Server. Server- och databasalternativ bör ändras sällan – för det mesta ställs dessa in vid installationen och lämnas ifred. Men då och då finns det goda skäl att göra en förändring – oavsett om det är prestandarelaterat, på grund av en ändring i applikationskoden, eller kanske för att något var felaktigt inställt första gången. Testa dessa ändringar först och fånga upp lämpliga mätvärden före och efter ändringen. Det verkar ganska okomplicerat och självklart, eller hur? Du kanske tror det, men om du inte har en förändringshanteringsprocess på plats som följs strikt så är det inte det.

I de flesta miljöer har mer än en person tillgång till SQL Server, och mer än en person har de privilegier som krävs för att ändra server- eller databasalternativ. Om fel inställning ändras kan prestandapåverkan bli betydande. (Har du någonsin oavsiktligt ställt in maxminnesinställningen till ett värde i GB istället för MB? Om du undrar, 128MB behövs inte för att en SQL Server-instans ska starta. Kolla in Ted Kruegers inlägg om hur du fixar detta , om du någonsin skulle göra det misstaget.) Andra ändringar kan skapa mindre problem som fortfarande är besvärliga och ibland svåra att spåra (att inaktivera Auto Create Statistics är ett bra exempel). Du kanske tror att dessa förändringar skulle vara väl kommunicerade (ibland är du så upptagen med att släcka bränder att du glömmer) eller lätt att lägga märke till (inte alltid). För att undvika detta spårar vi inställningarna och sedan, när vi kör våra regelbundna kontroller (eller när vi felsöker ett problem), verifierar vi att ingenting har ändrats.

Fånga data

Till skillnad från det tidigare inlägget om underhållsuppgifter, där vi förlitade oss på msdb för att hålla fast vid den data vi brydde oss om, måste vi ställa in datafångst till exempel och databasinställningar. Vi tar ögonblicksbilder av sys.configurations och sys.database_info dagligen till tabeller i vår Baselines-databas, och använder sedan frågor för att se om något har ändrats och när.

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

Skriptet för att skapa SQLskills_DBData-tabellen är kompatibelt med SQL Server 2014. För tidigare versioner kan du behöva ändra bastabell och ögonblicksbildsfråga (se nästa koduppsättning).

När du har skapat tabellerna skapar du ett jobb som kommer att utföra följande två frågor dagligen. Återigen, vi skulle inte förvänta oss att dessa alternativ skulle ändras mer än en gång om dagen, och även om vi hoppas att ingen skulle ändra en inställning och sedan ändra tillbaka den (därför skulle den inte dyka upp i en bild), så är det alltid en möjlighet . Om du upptäcker att denna datafångst inte passar dina behov, eftersom inställningarna ändras ofta eller tillfälligt, kanske du vill implementera en utlösare eller använda revision.

För att redigera serveralternativ via (sp_configure) behöver en inloggning ALTER SETTINGS servernivåbehörighet, som ingår om du är medlem i rollerna sysadmin eller serveradmin. För att redigera de flesta databasinställningar (ALTER DATABASE SET), behöver du ALTER-behörigheten i databasen, även om vissa alternativ kräver ytterligare rättigheter, såsom KONTROLLSERVER eller alternativet på servernivå 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

Söker efter ändringar

Nu när vi samlar in denna information, hur hittar vi ändringar? Eftersom vi vet att flera inställningar kan ändras, och på olika datum, behöver vi en metod som tittar på varje rad. Detta är inte svårt att göra, men det genererar inte den vackraste koden. För serveralternativ är det inte så illa:

;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

Ändrade instansinställningar

För databasalternativ är frågan i en lagrad procedur (eftersom den var så otymplig), som du kan ladda ner här. Så här kör du den lagrade proceduren:

EXEC dbo.usp_FindDBSettingChanges

Utdatan kommer att lista databasen och inställningen som ändrades, samt datum:

Ändrade databasinställningar

Du kan köra dessa frågor när prestandaproblem dyker upp för att snabbt kontrollera om några inställningar har ändrats, eller så kan du vara lite mer proaktiv och köra dem regelbundet i ett schemalagt jobb som meddelar dig om något har ändrats. Jag inkluderade inte T-SQL-koden för att skicka ett e-postmeddelande med databaspost om det finns en förändring, men det kommer inte att vara svårt att göra baserat på koden som tillhandahålls här.

Använda Performance Advisor

SQL Sentry Performance Advisor spårar inte denna information som standard, men du kan fortfarande fånga informationen i en databas, sedan låta PA kontrollera om några inställningar har ändrats och meddela dig om de har ändrats. För att ställa in detta skapar du tabellerna SQLskills_ConfigData och SQLskillsDBData och ställer in det schemalagda jobbet så att det infogas i dessa tabeller regelbundet. Inom SQL Sentry-klienten, ställ in ett anpassat villkor, som vi gjorde i ett tidigare inlägg i den här serien, Proactive SQL Server Health Checks, Part 1:Disk Space post.

Inom det anpassade villkoret har du två alternativ. Först kan du bara köra den angivna koden som kontrollerar historiska data för att se om något har förändrats (och sedan skicka ett meddelande i så fall). Att kontrollera historiska data för förändring är något som du skulle köra dagligen, som du skulle göra med ett agentjobb. Alternativt kan du vara mer proaktiv och jämföra aktuella löpande värden mot de senaste uppgifterna oftare, t.ex. en gång i timmen för att leta efter förändringar. Exempelkod för att kontrollera de aktuella inställningarna för instansen mot den senaste insamlingen:

;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

Sammanfattning

Att kontrollera instans- och databasalternativ är enkelt och uppenbart, och i vissa situationer kan denna historiska information spara mycket tid vid felsökning. Om du inte fångar den här informationen någonstans rekommenderar jag dig att börja; det är alltid bättre att proaktivt leta efter problem än att reagera när du är brandbekämpande och potentiellt stressad, osäker på vad som orsakar problem i din produktionsmiljö.


  1. MySQL-fel:nyckelspecifikation utan nyckellängd

  2. Oracle 11g på Mac OS X

  3. Tecken på att din databas inte längre fungerar för dig

  4. Skillnad mellan DECIMAL och NUMERIC datatyp i PSQL