sql >> Database teknologi >  >> RDS >> Sqlserver

Proaktive SQL Server-sundhedstjek, del 3:Forekomst- og databaseindstillinger

Vores diskussion af proaktive opgaver, der holder din database sund, fortsætter i dette indlæg, mens vi tackler server- og databasemuligheder. Du tænker måske allerede, at dette vil være et hurtigt indlæg - hvem ændrer server- eller databaseindstillinger så ofte? Du ville blive overrasket, især hvis du har mange mennesker, der har adgang til SQL Server. Server- og databaseindstillinger bør ændres sjældent - for det meste indstilles disse ved installation og efterlades alene. Men en gang imellem er der god grund til at lave en ændring – det være sig ydelsesrelateret, på grund af en ændring i applikationskoden, eller måske fordi noget blev indstillet forkert første gang. Test disse ændringer først, og indfang passende metrics før og efter ændringen. Det virker ret ligetil og indlysende, ikke? Det tror du måske, men hvis du ikke har en forandringsledelsesproces på plads, som følges nøje, er den ikke det.

I de fleste miljøer har mere end én person adgang til SQL Server, og mere end én person har de nødvendige rettigheder til at ændre server- eller databaseindstillinger. Hvis den forkerte indstilling ændres, kan præstationspåvirkningen være betydelig. (Har du nogensinde utilsigtet indstillet den maksimale hukommelsesindstilling til en værdi i GB i stedet for MB? Hvis du undrer dig, er 128 MB ikke nok hukommelse nødvendig til, at en SQL Server-instans kan starte. Tjek Ted Kruegers indlæg om, hvordan du løser dette , hvis du nogensinde skulle begå den fejl.) Andre ændringer kan skabe mindre problemer, der stadig er besværlige og nogle gange svære at spore (deaktivering af Automatisk oprettelse af statistik er et godt eksempel). Du tror måske, at disse ændringer ville være godt kommunikeret (nogle gange har du så travlt med at slukke brande, du glemmer) eller nemme at bemærke (ikke altid). For at undgå dette sporer vi indstillingerne, og når vi kører vores regelmæssige kontroller (eller ved fejlfinding af et problem), bekræfter vi, at intet er ændret.

Optagelse af data

I modsætning til det tidligere indlæg om vedligeholdelsesopgaver, hvor vi stolede på msdb til at holde på de data, vi bekymrede os om, er vi nødt til at opsætte datafangst for eksempel og databaseindstillinger. Vi tager dagligt et øjebliksbillede af sys.configurations og sys.database_info til tabeller i vores Baselines-database og bruger derefter forespørgsler til at se, om noget er ændret, og hvornå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

Scriptet til at oprette SQLskills_DBData-tabellen er kompatibelt med SQL Server 2014. For tidligere versioner skal du muligvis ændre basistabel og snapshot-forespørgsel (se næste sæt kode).

Når du har oprettet tabellerne, skal du oprette et job, der udfører følgende to forespørgsler dagligt. Igen, vi ville ikke forvente, at disse muligheder ville ændre sig mere end én gang om dagen, og selvom vi håber, at ingen ville ændre en indstilling, så ændre den tilbage (derfor ville den ikke dukke op i en optagelse), er det altid en mulighed . Hvis du opdager, at denne datafangst ikke passer til dine behov, fordi indstillingerne ændres ofte eller midlertidigt, kan du implementere en trigger eller bruge revision.

For at redigere serverindstillinger via (sp_configure), skal et login have ALTER SETTINGS-tilladelsen på serverniveau, som er inkluderet, hvis du er medlem af rollerne sysadmin eller serveradmin. For at redigere de fleste databaseindstillinger (ALTER DATABASE SET), skal du have ALTER-tilladelsen i databasen, selvom nogle muligheder kræver yderligere rettigheder, såsom KONTROLSERVER eller indstillingen ALTER ANY DATABASE på serverniveau.

/* 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øger efter ændringer

Nu hvor vi fanger disse oplysninger, hvordan finder vi ændringer? Når vi ved, at der kan ændres flere indstillinger, og på forskellige datoer, har vi brug for en metode, der ser på hver række. Dette er ikke svært at gøre, men det genererer ikke den smukkeste kode. For serverindstillinger er det ikke så slemt:

;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

Ændrede instansindstillinger

For databasemuligheder er forespørgslen i en lagret procedure (fordi den var så uhåndterlig), som du kan downloade her. Sådan kører du den lagrede procedure:

EXEC dbo.usp_FindDBSettingChanges

Outputtet vil vise databasen og den ændrede indstilling samt datoen:

Ændrede databaseindstillinger

Du kan køre disse forespørgsler, når der opstår problemer med ydeevnen, for hurtigt at kontrollere, om nogen indstillinger er ændret, eller du kan være lidt mere proaktiv og køre dem regelmæssigt i et planlagt job, der giver dig besked, hvis noget er ændret. Jeg inkluderede ikke T-SQL-koden til at sende en e-mail ved hjælp af databasemail, hvis der er en ændring, men det vil ikke være svært at gøre baseret på koden, der er angivet her.

Brug af Performance Advisor

SQL Sentry Performance Advisor sporer ikke disse oplysninger som standard, men du kan stadig fange oplysningerne i en database, derefter få PA til at kontrollere, om nogen indstillinger er ændret, og give dig besked, hvis de har. For at konfigurere dette skal du oprette tabellerne SQLskills_ConfigData og SQLskillsDBData og konfigurere det planlagte job til at indsætte i disse tabeller regelmæssigt. Inden for SQL Sentry-klienten skal du opsætte en brugerdefineret tilstand, som vi gjorde i et tidligere indlæg i denne serie, Proactive SQL Server Health Checks, Part 1:Disk Space post.

Inden for den brugerdefinerede tilstand har du to muligheder. Først kan du bare udføre den angivne kode, som kontrollerer historiske data for at se, om noget er ændret (og derefter sende en meddelelse, hvis det er tilfældet). At tjekke historiske data for ændringer er noget, du vil køre dagligt, som du ville gøre med et agentjob. Alternativt kan du være mere proaktiv og sammenligne aktuelle, løbende værdier med de seneste data på et hyppigere grundlag, f.eks. en gang i timen for at se efter ændringer. Eksempelkode til at kontrollere de aktuelle indstillinger for forekomsten i forhold til den seneste optagelse:

;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

Oversigt

Det er ligetil og indlysende at kontrollere instans- og databaseindstillinger, og i nogle situationer kan disse historiske oplysninger spare dig betydelig tid ved fejlfinding. Hvis du ikke fanger disse oplysninger nogen steder, opfordrer jeg dig til at starte; det er altid bedre proaktivt at lede efter problemer end at reagere, når du er i brandslukning og potentielt stresset, usikker på, hvad der forårsager et problem i dit produktionsmiljø.


  1. Sådan importeres en database ved hjælp af kommandolinjen

  2. SQL GROUP BY-klausul for begyndere

  3. Sådan får du SQLite db-værdier i Arraylist til Listview

  4. Sådan returneres forespørgselsresultater som en kommasepareret liste i SQL Server – STRING_AGG()