Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Verificações de integridade proativas do SQL Server, Parte 3:Configurações de instância e banco de dados


Nossa discussão sobre tarefas proativas que mantêm seu banco de dados saudável continua neste post à medida que abordamos as opções de servidor e banco de dados. Você já deve estar pensando que este será um post rápido – quem muda as opções de servidor ou banco de dados com tanta frequência? Você ficaria surpreso, especialmente se tiver muitas pessoas que têm acesso ao SQL Server. As opções de servidor e banco de dados devem ser alteradas com pouca frequência – na maioria das vezes, elas são definidas na instalação e deixadas sozinhas. Mas de vez em quando há uma boa razão para fazer uma alteração – seja relacionada ao desempenho, devido a uma alteração no código do aplicativo ou talvez porque algo foi definido incorretamente na primeira vez. Teste essas alterações primeiro e capture as métricas apropriadas antes e depois da alteração. Parece bastante simples e óbvio, certo? Você pode pensar assim, mas se você não tiver um processo de gerenciamento de mudanças que seja seguido rigorosamente, não é.

Na maioria dos ambientes, mais de uma pessoa tem acesso ao SQL Server e mais de uma pessoa tem os privilégios necessários para alterar as opções de servidor ou banco de dados. Se a configuração errada for alterada, o impacto no desempenho pode ser significativo. (Você já definiu inadvertidamente a configuração de memória máxima para um valor em GB em vez de MB? Caso você esteja se perguntando, 128 MB não é memória suficiente para iniciar uma instância do SQL Server. Confira a postagem de Ted Krueger sobre como corrigir isso , caso você cometa esse erro.) Outras alterações podem criar problemas menores que ainda são problemáticos e às vezes difíceis de rastrear (desativar a criação automática de estatísticas é um bom exemplo). Você pode pensar que essas mudanças seriam bem comunicadas (às vezes você está tão ocupado apagando incêndios que esquece) ou fáceis de perceber (nem sempre). Para evitar isso, rastreamos as configurações e, ao executar nossas verificações regulares (ou ao solucionar um problema), verificamos se nada mudou.

Capturando os dados


Ao contrário do post anterior sobre tarefas de manutenção, onde contamos com o msdb para manter os dados que nos interessam, temos que configurar a captura de dados, por exemplo, e as configurações do banco de dados. Faremos um instantâneo de sys.configurations e sys.database_info diariamente em tabelas em nosso banco de dados de linhas de base e, em seguida, usaremos consultas para ver se algo mudou 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

O script para criar a tabela SQLskills_DBData é compatível com o SQL Server 2014. Para versões anteriores, pode ser necessário modificar a tabela base e a consulta de instantâneo (consulte o próximo conjunto de códigos).

Depois de criar as tabelas, crie um trabalho que executará as duas consultas a seguir diariamente. Novamente, não esperamos que essas opções mudem mais de uma vez por dia e, embora esperemos que ninguém altere uma configuração, depois a altere novamente (portanto, ela não aparecerá em uma captura), isso é sempre uma possibilidade . Se você achar que essa captura de dados não atende às suas necessidades porque as configurações mudam com frequência ou temporariamente, convém implementar um acionador ou usar a auditoria.

Para editar as opções do servidor por meio de (sp_configure), um logon precisa da permissão de nível de servidor ALTER SETTINGS, que está incluída se você for membro das funções sysadmin ou serveradmin. Para editar a maioria das configurações de banco de dados (ALTER DATABASE SET), você precisa da permissão ALTER no banco de dados, embora algumas opções exijam direitos adicionais, como CONTROL SERVER ou a opção de nível de servidor 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

Verificando alterações


Agora que estamos capturando essas informações, como encontramos mudanças? Sabendo que pode haver várias configurações alteradas e em datas diferentes, precisamos de um método que analise cada linha. Isso não é difícil de fazer, mas não gera o código mais bonito. Para opções de servidor, não é tão ruim:
;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

Configurações de instância alteradas

Para opções de banco de dados, a consulta está em um procedimento armazenado (porque era muito complicado), que você pode baixar aqui. Para executar o procedimento armazenado:
EXEC dbo.usp_FindDBSettingChanges

A saída listará o banco de dados e a configuração que foi alterada, bem como a data:

Configurações do banco de dados alteradas

Você pode executar essas consultas quando surgirem problemas de desempenho, para verificar rapidamente se alguma configuração foi alterada, ou pode ser um pouco mais proativo e executá-las regularmente em um trabalho agendado que o notifique se algo mudou. Não incluí o código T-SQL para enviar um e-mail usando o correio do banco de dados se houver uma alteração, mas isso não será difícil de fazer com base no código fornecido aqui.

Usando o Performance Advisor


O SQL Sentry Performance Advisor não rastreia essas informações por padrão, mas você ainda pode capturar as informações em um banco de dados e, em seguida, fazer o PA verificar se alguma configuração foi alterada e notificá-lo se houver. Para configurar isso, crie as tabelas SQLskills_ConfigData e SQLskillsDBData e configure o trabalho agendado para inserir nessas tabelas regularmente. No cliente SQL Sentry, configure uma Condição Personalizada, como fizemos em uma postagem anterior desta série, Verificações de integridade proativas do SQL Server, Parte 1:postagem de espaço em disco.

Dentro da Condição Personalizada, você tem duas opções. Primeiro, você pode simplesmente executar o código fornecido, que verifica os dados históricos para ver se alguma coisa mudou (e, em seguida, enviar uma notificação, se for o caso). A verificação de dados históricos para alterações é algo que você executaria diariamente, como faria com um trabalho de agente. Como alternativa, você pode ser mais proativo e comparar os valores atuais em execução com os dados mais recentes com mais frequência, por exemplo, uma vez por hora, para procurar mudanças. Código de exemplo para verificar as configurações atuais da instância em relação à captura mais 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

Resumo


A verificação das opções de instância e banco de dados é simples e óbvia e, em algumas situações, essas informações históricas podem economizar um tempo significativo na solução de problemas. Se você não está capturando essas informações em nenhum lugar, eu o encorajo a começar; é sempre melhor procurar proativamente por problemas do que reagir quando você está combatendo incêndios e potencialmente estressado, sem saber o que está causando um problema em seu ambiente de produção.