sql >> Base de Datos >  >> RDS >> Sqlserver

Comprobaciones de estado proactivas de SQL Server, parte 3:configuración de instancias y bases de datos

Nuestra discusión sobre las tareas proactivas que mantienen su base de datos en buen estado continúa en esta publicación mientras abordamos las opciones de servidor y base de datos. Es posible que ya esté pensando que esta será una publicación rápida:¿quién cambia las opciones de servidor o base de datos con tanta frecuencia? Se sorprendería, especialmente si tiene muchas personas que tienen acceso a SQL Server. Las opciones del servidor y la base de datos deben cambiar con poca frecuencia; en su mayor parte, se configuran en la instalación y se dejan como están. Pero de vez en cuando hay una buena razón para hacer un cambio, ya sea relacionado con el rendimiento, debido a un cambio en el código de la aplicación o quizás porque algo se configuró incorrectamente la primera vez. Pruebe estos cambios primero y capture las métricas adecuadas antes y después del cambio. Parece bastante sencillo y obvio, ¿verdad? Puede pensar que sí, pero si no cuenta con un proceso de gestión de cambios que se siga estrictamente, no lo es.

En la mayoría de los entornos, más de una persona tiene acceso a SQL Server y más de una persona tiene los privilegios necesarios para cambiar las opciones del servidor o la base de datos. Si se cambia la configuración incorrecta, el impacto en el rendimiento puede ser significativo. (¿Alguna vez, sin darse cuenta, estableció la configuración de memoria máxima en un valor en GB en lugar de MB? En caso de que se lo esté preguntando, 128 MB no es suficiente memoria necesaria para que se inicie una instancia de SQL Server. Consulte la publicación de Ted Krueger sobre cómo solucionar este problema. , si alguna vez comete ese error.) Otros cambios pueden crear problemas más pequeños que siguen siendo problemáticos y, a veces, difíciles de rastrear (deshabilitar la creación automática de estadísticas es un buen ejemplo). Puede pensar que estos cambios serían bien comunicados (a veces está tan ocupado apagando incendios que se olvida) o fáciles de notar (no siempre). Para evitar esto, hacemos un seguimiento de la configuración y luego, cuando realizamos nuestras comprobaciones periódicas (o cuando solucionamos un problema), verificamos que nada haya cambiado.

Capturar los datos

A diferencia de la publicación anterior sobre tareas de mantenimiento, donde confiamos en msdb para mantener los datos que nos importan, tenemos que configurar la captura de datos para la configuración de la instancia y la base de datos. Tomaremos una instantánea de sys.configurations y sys.database_info diariamente en las tablas de nuestra base de datos Baselines, luego usaremos consultas para ver si algo cambió y cuándo.

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

El script para crear la tabla SQLskills_DBData es compatible con SQL Server 2014. Para versiones anteriores, es posible que deba modificar la tabla base y la consulta de instantáneas (consulte el siguiente conjunto de código).

Una vez que haya creado las tablas, cree un trabajo que ejecutará las siguientes dos consultas diariamente. Una vez más, no esperaríamos que estas opciones cambiaran más de una vez al día, y aunque esperamos que nadie cambie una configuración y luego la vuelva a cambiar (por lo tanto, no aparecería en una captura), esa siempre es una posibilidad. . Si encuentra que esta captura de datos no se ajusta a sus necesidades, porque la configuración cambia con frecuencia o temporalmente, es posible que desee implementar un activador o utilizar la auditoría.

Para editar las opciones del servidor a través de (sp_configure), un inicio de sesión necesita el permiso de nivel de servidor ALTER SETTINGS, que se incluye si es miembro de los roles de administrador del sistema o administrador del servidor. Para editar la mayoría de las configuraciones de la base de datos (ALTER DATABASE SET), necesita el permiso ALTER en la base de datos, aunque algunas opciones requieren derechos adicionales, como CONTROL SERVER o la opción de nivel de servidor ALTER CUALQUIER BASE DE DATOS.

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

Comprobación de cambios

Ahora que estamos capturando esta información, ¿cómo encontramos los cambios? Sabiendo que se pueden cambiar varias configuraciones y en diferentes fechas, necesitamos un método que analice cada fila. Esto no es difícil de hacer, pero no genera el código más bonito. Para las opciones del servidor, no está tan 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

Configuración de instancia modificada

Para las opciones de la base de datos, la consulta está en un procedimiento almacenado (porque era muy difícil de manejar), que puede descargar aquí. Para ejecutar el procedimiento almacenado:

EXEC dbo.usp_FindDBSettingChanges

El resultado mostrará la base de datos y la configuración que cambió, así como la fecha:

Configuración de base de datos modificada

Puede ejecutar estas consultas cuando surjan problemas de rendimiento, para verificar rápidamente si alguna configuración ha cambiado, o podría ser un poco más proactivo y ejecutarlas regularmente en un trabajo programado que le notifique si algo ha cambiado. No incluí el código T-SQL para enviar un correo electrónico usando el correo de la base de datos si hay un cambio, pero eso no será difícil de hacer según el código proporcionado aquí.

Uso del Asesor de rendimiento

SQL Sentry Performance Advisor no rastrea esta información de forma predeterminada, pero aún puede capturar la información en una base de datos, luego hacer que PA verifique si alguna configuración ha cambiado y le notifique si es así. Para configurar esto, cree las tablas SQLskills_ConfigData y SQLskillsDBData y configure el trabajo programado para insertarlo en esas tablas de manera regular. Dentro del cliente de SQL Sentry, configure una Condición personalizada, como hicimos en una publicación anterior de esta serie, Comprobaciones de estado proactivas de SQL Server, Parte 1:Publicación de espacio en disco.

Dentro de la Condición personalizada, tiene dos opciones. Primero, puede simplemente ejecutar el código provisto que verifica los datos históricos para ver si algo ha cambiado (y luego enviar una notificación si es así). La verificación de cambios en los datos históricos es algo que ejecutaría a diario, como lo haría con un trabajo de agente. Alternativamente, podría ser más proactivo y comparar los valores actuales y en ejecución con los datos más recientes con mayor frecuencia, p. una vez por hora, para buscar cambios. Código de ejemplo para comparar la configuración actual de la instancia con la captura más reciente:

;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

Resumen

Verificar las opciones de la instancia y la base de datos es sencillo y obvio, y en algunas situaciones esta información histórica puede ahorrarle mucho tiempo al solucionar problemas. Si no está capturando esta información en ninguna parte, lo animo a que comience; siempre es mejor buscar problemas de manera proactiva que reaccionar cuando está combatiendo un incendio y potencialmente estresado, sin saber qué está causando un problema en su entorno de producción.