Pensé que esto se resolvió con el siguiente enlace, el trabajo alrededor funciona, pero el parche no. Trabajando con el soporte de Microsoft para resolver.
http://support.microsoft.com/kb/2606883
Ok, tengo un problema que quería lanzar a StackOverflow para ver si alguien tiene una idea.
Tenga en cuenta que esto es con SQL Server 2008 R2
Problema: la eliminación de 3000 registros de una tabla con 15000 registros demora de 3 a 4 minutos cuando se activa un activador y solo de 3 a 5 segundos cuando el activador está desactivado.
Configuración de la mesa
Dos tablas que llamaremos Principal y Secundaria. Secundaria contiene registros de los elementos que deseo eliminar, por lo que cuando realizo la eliminación, me uno a la tabla Secundaria. Se ejecuta un proceso antes de la declaración de eliminación para completar la tabla secundaria con los registros que se eliminarán.
Eliminar declaración:
DELETE FROM MAIN
WHERE ID IN (
SELECT Secondary.ValueInt1
FROM Secondary
WHERE SECONDARY.GUID = '9FFD2C8DD3864EA7B78DA22B2ED572D7'
);
Esta tabla tiene muchas columnas y alrededor de 14 índices NC diferentes. Probé un montón de cosas diferentes antes de determinar que el desencadenante era el problema.
- Activa el bloqueo de página (lo hemos desactivado por defecto)
- Estadísticas recopiladas manualmente
- Desactivado auto recopilación de estadísticas
- Índice verificado de salud y fragmentación
- Quitó el índice agrupado de la tabla
- Se examinó el plan de ejecución (nada se mostraba como índices faltantes y el costo era del 70 por ciento hacia la eliminación real con aproximadamente el 28 por ciento para la combinación / fusión de los registros
Disparadores
La tabla tiene 3 disparadores (uno para cada operación de inserción, actualización y eliminación). Modifiqué el código para que el disparador de eliminación simplemente regresara, luego seleccioné uno para ver cuántas veces se disparó. Solo se dispara una vez durante toda la operación (como se esperaba).
ALTER TRIGGER [dbo].[TR_MAIN_RD] ON [dbo].[MAIN]
AFTER DELETE
AS
SELECT 1
RETURN
Recordar
- Con Trigger activado: la declaración tarda de 3 a 4 minutos en completarse
- Con Trigger desactivado: la declaración tarda de 3 a 5 segundos en completarse
Alguien tiene alguna idea de por qué?
También tenga en cuenta que no busca cambiar esta arquitectura, agregar índices de eliminación, etc. como solución. Esta tabla es la pieza central de algunas operaciones de datos importantes y tuvimos que ajustarla y ajustarla (índices, bloqueo de página, etc.) para permitir que las principales operaciones de concurrencia funcionen sin puntos muertos.
Aquí está el plan de ejecución xml (los nombres fueron cambiados para proteger a los inocentes)
<?xml version="1.0" encoding="utf-16"?>
<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.1" Build="10.50.1790.0" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple StatementCompId="1" StatementEstRows="185.624" StatementId="1" StatementOptmLevel="FULL" StatementOptmEarlyAbortReason="GoodEnoughPlanFound" StatementSubTreeCost="0.42706" StatementText="DELETE FROM MAIN WHERE ID IN (SELECT Secondary.ValueInt1 FROM Secondary WHERE Secondary.SetTMGUID = '9DDD2C8DD3864EA7B78DA22B2ED572D7')" StatementType="DELETE" QueryHash="0xAEA68D887C4092A1" QueryPlanHash="0x78164F2EEF16B857">
<StatementSetOptions ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" ARITHABORT="false" CONCAT_NULL_YIELDS_NULL="true" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="true" />
<QueryPlan CachedPlanSize="48" CompileTime="20" CompileCPU="20" CompileMemory="520">
<RelOp AvgRowSize="9" EstimateCPU="0.00259874" EstimateIO="0.296614" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="185.624" LogicalOp="Delete" NodeId="0" Parallel="false" PhysicalOp="Clustered Index Delete" EstimatedTotalSubtreeCost="0.42706">
<OutputList />
<Update WithUnorderedPrefetch="true" DMLRequestSort="false">
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_02]" IndexKind="Clustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[PK_MAIN_ID]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[UK_MAIN_01]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_03]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_04]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_05]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_06]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_07]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_08]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_09]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_10]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_11]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[UK_MAIN_12]" IndexKind="NonClustered" />
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_13]" IndexKind="NonClustered" />
<RelOp AvgRowSize="15" EstimateCPU="1.85624E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="185.624" LogicalOp="Top" NodeId="2" Parallel="false" PhysicalOp="Top" EstimatedTotalSubtreeCost="0.127848">
<OutputList>
<ColumnReference Column="Uniq1002" />
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="RelationshipID" />
</OutputList>
<Top RowCount="true" IsPercent="false" WithTies="false">
<TopExpression>
<ScalarOperator ScalarString="(0)">
<Const ConstValue="(0)" />
</ScalarOperator>
</TopExpression>
<RelOp AvgRowSize="15" EstimateCPU="0.0458347" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="185.624" LogicalOp="Left Semi Join" NodeId="3" Parallel="false" PhysicalOp="Merge Join" EstimatedTotalSubtreeCost="0.12783">
<OutputList>
<ColumnReference Column="Uniq1002" />
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="RelationshipID" />
</OutputList>
<Merge ManyToMany="false">
<InnerSideJoinColumns>
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Column="ValueInt1" />
</InnerSideJoinColumns>
<OuterSideJoinColumns>
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="ID" />
</OuterSideJoinColumns>
<Residual>
<ScalarOperator ScalarString="[MyDatabase].[dbo].[MAIN].[ID]=[MyDatabase].[dbo].[Secondary].[ValueInt1]">
<Compare CompareOp="EQ">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="ID" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Column="ValueInt1" />
</Identifier>
</ScalarOperator>
</Compare>
</ScalarOperator>
</Residual>
<RelOp AvgRowSize="19" EstimateCPU="0.0174567" EstimateIO="0.0305324" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="15727" LogicalOp="Index Scan" NodeId="4" Parallel="false" PhysicalOp="Index Scan" EstimatedTotalSubtreeCost="0.0479891" TableCardinality="15727">
<OutputList>
<ColumnReference Column="Uniq1002" />
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="ID" />
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="RelationshipID" />
</OutputList>
<IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" NoExpandHint="false">
<DefinedValues>
<DefinedValue>
<ColumnReference Column="Uniq1002" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="ID" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="RelationshipID" />
</DefinedValue>
</DefinedValues>
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[PK_MAIN_ID]" IndexKind="NonClustered" />
</IndexScan>
</RelOp>
<RelOp AvgRowSize="11" EstimateCPU="0.00392288" EstimateIO="0.03008" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="3423.53" LogicalOp="Index Seek" NodeId="5" Parallel="false" PhysicalOp="Index Seek" EstimatedTotalSubtreeCost="0.0340029" TableCardinality="171775">
<OutputList>
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Column="ValueInt1" />
</OutputList>
<IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" NoExpandHint="false">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Column="ValueInt1" />
</DefinedValue>
</DefinedValues>
<Object Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Index="[IX_Secondary_01]" IndexKind="NonClustered" />
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<Prefix ScanType="EQ">
<RangeColumns>
<ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Column="SetTMGUID" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="'9DDD2C8DD3864EA7B78DA22B2ED572D7'">
<Const ConstValue="'9DDD2C8DD3864EA7B78DA22B2ED572D7'" />
</ScalarOperator>
</RangeExpressions>
</Prefix>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
</IndexScan>
</RelOp>
</Merge>
</RelOp>
</Top>
</RelOp>
</Update>
</RelOp>
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>
Bueno, aquí está la respuesta oficial de Microsoft ... que creo que es un gran defecto de diseño.
14/11/2011 - La respuesta oficial ha cambiado. No están utilizando el registro de transacciones como se indicó anteriormente. Están utilizando el almacén interno (nivel de fila) para copiar los datos modificados. Todavía no pueden determinar por qué lleva tanto tiempo.
Decidimos utilizar desencadenantes en lugar de en lugar de desencadenantes después de eliminar.
La parte DESPUÉS del desencadenante hace que tengamos que leer el registro de transacciones después de que se completen las eliminaciones y compile la tabla insertada / eliminada del desencadenante. Aquí es donde pasamos la gran cantidad de tiempo y es por diseño para la parte DESPUÉS del disparador. INSTEAD OF trigger evitaría este comportamiento de escanear el registro de transacciones y construir una tabla insertada / eliminada. Además, como se observó, las cosas son mucho más rápidas si eliminamos todas las columnas con nvarchar (max), lo que tiene sentido debido al hecho de que se considera datos LOB. Consulte el siguiente artículo para obtener más información sobre los datos en fila:
http://msdn.microsoft.com/en-us/library/ms189087.aspx
Resumen: DESPUÉS de que el disparador requiera escanear nuevamente el registro de transacciones después de que finalice la eliminación, entonces tenemos que construir e insertar / eliminar la tabla que requiere un mayor uso del registro de transacciones y el tiempo.
Entonces, como plan de acción, esto es lo que sugerimos en este momento:
fuente
Según el plan, todo va correctamente. Puede intentar escribir la eliminación como una UNIÓN en lugar de una IN que le dará un plan diferente.
Sin embargo, no estoy seguro de cuánto ayudará. Cuando la eliminación se ejecuta con los desencadenantes en la tabla, ¿cuál es el tipo de espera para la sesión que realiza la eliminación?
fuente