sql >> Base de Datos >  >> RDS >> Database

Una mirada a DBCC CHECKCONSTRAINTS y ​​E/S

Un elemento común utilizado en el diseño de bases de datos es la restricción. Las restricciones vienen en una variedad de sabores (por ejemplo, predeterminadas, únicas) y hacen cumplir la integridad de las columnas en las que existen. Cuando se implementan bien, las restricciones son un componente poderoso en el diseño de una base de datos porque evitan que los datos que no cumplen con los criterios establecidos entren en una base de datos. Sin embargo, las restricciones se pueden violar usando comandos como WITH NOCHECK y IGNORE_CONSTRAINTS . Además, al usar el REPAIR_ALLOW_DATA_LOSS opción con cualquier DBCC CHECK comando para reparar la corrupción de la base de datos, las restricciones no se consideran.

En consecuencia, es posible tener datos no válidos en la base de datos, ya sea datos que no se adhieren a una restricción o datos que ya no mantienen la relación esperada de clave principal y clave externa. SQL Server incluye DBCC CHECKCONSTRAINTS instrucción para encontrar datos que violen las restricciones. Después de que se ejecute cualquier opción de reparación, ejecute DBCC CHECKCONSTRAINTS para toda la base de datos para garantizar que no haya problemas, y puede haber momentos en los que sea apropiado ejecutar CHECKCONSTRAINTS para una restricción de selección o una tabla. Mantener la integridad de los datos es fundamental y, aunque no es normal ejecutar DBCC CHECKCONSTRAINTS de forma programada para encontrar datos no válidos, cuando necesite ejecutarlos, es una buena idea comprender el impacto en el rendimiento que puede crear.

DBCC CHECKCONSTRAINTS puede ejecutarse para una sola restricción, una tabla o toda la base de datos. Al igual que otros comandos de verificación, puede llevar mucho tiempo completarlo y consumirá recursos del sistema, especialmente para bases de datos más grandes. A diferencia de otros comandos de verificación, CHECKCONSTRAINTS no utiliza una instantánea de la base de datos.

Con Extended Events podemos examinar el uso de recursos cuando ejecutamos DBCC CHECKCONSTRAINTS para la mesa Para mostrar mejor el impacto, ejecuté el script Create Enlarged AdventureWorks Tables.sql de Jonathan Kehayias (blog | @SQLPoolBoy) para crear tablas más grandes. La secuencia de comandos de Jonathan solo crea los índices para las tablas, por lo que las siguientes declaraciones son necesarias para agregar algunas restricciones seleccionadas:

USE [AdventureWorks2012];
GO
 
ALTER TABLE [Sales].[SalesOrderDetailEnlarged]
WITH CHECK ADD CONSTRAINT [FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID]
FOREIGN KEY([SalesOrderID])
REFERENCES [Sales].[SalesOrderHeaderEnlarged] ([SalesOrderID])
ON DELETE CASCADE;
GO
 
ALTER TABLE [Sales].[SalesOrderDetailEnlarged]
WITH CHECK ADD CONSTRAINT [CK_SalesOrderDetailEnlarged_OrderQty]
CHECK (([OrderQty]>(0)))
GO
 
ALTER TABLE [Sales].[SalesOrderDetailEnlarged]
WITH CHECK ADD CONSTRAINT [CK_SalesOrderDetailEnlarged_UnitPrice]
CHECK (([UnitPrice]>=(0.00)));
GO
 
ALTER TABLE [Sales].[SalesOrderHeaderEnlarged]
WITH CHECK ADD CONSTRAINT [CK_SalesOrderHeaderEnlarged_DueDate]
CHECK (([DueDate]>=[OrderDate]))
GO
 
ALTER TABLE [Sales].[SalesOrderHeaderEnlarged]
WITH CHECK ADD CONSTRAINT [CK_SalesOrderHeaderEnlarged_Freight]
CHECK (([Freight]>=(0.00)))
GO

Podemos verificar qué restricciones existen usando sp_helpconstraint :

EXEC sp_helpconstraint '[Sales].[SalesOrderDetailEnlarged]';
GO


salida de sp_helpconstraint

Una vez que existen las restricciones, podemos comparar el uso de recursos para DBCC CHECKCONSTRAINTS para una sola restricción, una tabla y toda la base de datos utilizando eventos extendidos. Primero crearemos una sesión que simplemente capture sp_statement_completed eventos, incluye el sql_text y envía la salida al ring_buffer :

CREATE EVENT SESSION [Constraint_Performance] ON SERVER
ADD EVENT sqlserver.sp_statement_completed
(
  ACTION(sqlserver.database_id,sqlserver.sql_text)
)
ADD TARGET package0.ring_buffer
(
  SET max_events_limit=(5000)
)
WITH 
(
    MAX_MEMORY=32768 KB, EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,
    MAX_DISPATCH_LATENCY=30 SECONDS, MAX_EVENT_SIZE=0 KB,
    MEMORY_PARTITION_MODE=NONE, TRACK_CAUSALITY=OFF, STARTUP_STATE=OFF
);
GO

A continuación, iniciaremos la sesión y ejecutaremos cada uno de los DBCC CHECKCONSTRAINT comandos, luego envíe el búfer de anillo a una tabla temporal para manipularlo. Tenga en cuenta que DBCC DROPCLEANBUFFERS se ejecuta antes de cada verificación para que cada una comience desde el caché frío, manteniendo un campo de prueba de nivel.

ALTER EVENT SESSION [Constraint_Performance]
ON SERVER
STATE=START;
GO
 
USE [AdventureWorks2012];
GO
 
DBCC DROPCLEANBUFFERS;
GO
DBCC CHECKCONSTRAINTS ('[Sales].[CK_SalesOrderDetailEnlarged_OrderQty]') WITH NO_INFOMSGS;
GO
DBCC DROPCLEANBUFFERS;
GO
DBCC CHECKCONSTRAINTS ('[Sales].[FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID]') WITH NO_INFOMSGS;
GO
DBCC DROPCLEANBUFFERS;
GO
DBCC CHECKCONSTRAINTS ('[Sales].[SalesOrderDetailEnlarged]') WITH NO_INFOMSGS;
GO
DBCC DROPCLEANBUFFERS;
GO
DBCC CHECKCONSTRAINTS WITH ALL_CONSTRAINTS, NO_INFOMSGS;
GO
 
DECLARE @target_data XML;
 
SELECT @target_data = CAST(target_data AS XML)
  FROM sys.dm_xe_sessions AS s
  INNER JOIN sys.dm_xe_session_targets AS t 
  ON t.event_session_address = s.[address]
  WHERE s.name = N'Constraint_Performance'
  AND t.target_name = N'ring_buffer';
 
SELECT
  n.value('(@name)[1]', 'varchar(50)') AS event_name,
  DATEADD(HOUR ,DATEDIFF(HOUR, SYSUTCDATETIME(), SYSDATETIME()),n.value('(@timestamp)[1]', 'datetime2')) AS [timestamp],
  n.value('(data[@name="duration"]/value)[1]', 'bigint') AS duration,
  n.value('(data[@name="physical_reads"]/value)[1]', 'bigint') AS physical_reads,
  n.value('(data[@name="logical_reads"]/value)[1]', 'bigint') AS logical_reads,
  n.value('(action[@name="sql_text"]/value)[1]', 'varchar(max)') AS sql_text,
  n.value('(data[@name="statement"]/value)[1]', 'varchar(max)') AS [statement]
INTO #EventData
FROM @target_data.nodes('RingBufferTarget/event[@name=''sp_statement_completed'']') AS q(n);
GO
 
ALTER EVENT SESSION [Constraint_Performance]
ON SERVER
STATE=STOP;
GO

Analizando el ring_buffer en una tabla temporal puede llevar algo de tiempo adicional (alrededor de 20 segundos en mi máquina), pero la consulta repetida de los datos es más rápida desde una tabla temporal que a través de ring_buffer . Si observamos la salida, vemos que hay varias declaraciones ejecutadas para cada DBCC CHECKCONSTRAINTS :

SELECT *
FROM #EventData
WHERE [sql_text] LIKE 'DBCC%';


Salida de eventos extendidos

Uso de eventos extendidos para profundizar en el funcionamiento interno de CHECKCONSTRAINTS es una tarea interesante, pero lo que realmente nos interesa aquí es el consumo de recursos, específicamente E/S. Podemos agregar las physical_reads para cada comando de verificación para comparar la E/S:

SELECT [sql_text], SUM([physical_reads]) AS [Total Reads]
FROM #EventData
WHERE [sql_text] LIKE 'DBCC%'
GROUP BY [sql_text];


E/S agregada para comprobaciones

Para verificar una restricción, SQL Server tiene que leer los datos para encontrar filas que puedan violar la restricción. La definición de CK_SalesOrderDetailEnlarged_OrderQty la restricción es [OrderQty] > 0 . La restricción de clave externa, FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID , establece una relación en SalesOrderID entre [Sales].[SalesOrderHeaderEnlarged] y [Sales].[SalesOrderDetailEnlarged] mesas. Intuitivamente, podría parecer que verificar la restricción de clave externa requeriría más E/S, ya que SQL Server debe leer datos de dos tablas. Sin embargo, [SalesOrderID] existe en el nivel hoja de IX_SalesOrderHeaderEnlarged_SalesPersonID índice no agrupado en [Sales].[SalesOrderHeaderEnlarged] y en la tabla IX_SalesOrderDetailEnlarged_ProductID índice en [Sales].[SalesOrderDetailEnlarged] mesa. Como tal, SQL Server escanea esos dos índices para comparar el [SalesOrderID] valores entre las dos tablas. Esto requiere poco más de 19.000 lecturas. En el caso de CK_SalesOrderDetailEnlarged_OrderQty restricción, el [OrderQty] La columna no está incluida en ningún índice, por lo que se realiza un análisis completo del índice agrupado, lo que requiere más de 72 000 lecturas.

Cuando se verifican todas las restricciones de una tabla, los requisitos de E/S son más altos que si se verifica una sola restricción y aumentan nuevamente cuando se verifica toda la base de datos. En el ejemplo anterior, [Sales].[SalesOrderHeaderEnlarged] y [Sales].[SalesOrderDetailEnlarged] las tablas son desproporcionadamente más grandes que otras tablas en la base de datos. Esto no es raro en escenarios del mundo real; muy a menudo, las bases de datos tienen varias tablas grandes que comprenden una gran parte de la base de datos. Al ejecutar CHECKCONSTRAINTS para estas tablas, tenga en cuenta el consumo potencial de recursos necesarios para la comprobación. Ejecute comprobaciones fuera del horario laboral cuando sea posible para minimizar el impacto del usuario. En el caso de que los controles deban ejecutarse durante el horario comercial normal, comprender qué restricciones existen y qué índices existen para respaldar la validación puede ayudar a medir el efecto del control. Puede ejecutar comprobaciones en un entorno de prueba o desarrollo primero para comprender el impacto en el rendimiento, pero luego pueden existir variaciones según el hardware, datos comparables, etc. Y, por último, recuerde que cada vez que ejecute un comando de comprobación que incluya REPAIR_ALLOW_DATA_LOSS opción, siga la reparación con DBCC CHECKCONSTRAINTS . La reparación de la base de datos no tiene en cuenta ninguna restricción, ya que se corrige la corrupción, por lo que además de perder datos potencialmente, puede terminar con datos que violan una o más restricciones en su base de datos.