Dachte, dies wurde mit dem folgenden Link behoben - die Umgehung funktioniert - aber der Patch funktioniert nicht. Arbeiten mit dem Microsoft-Support zur Lösung.
http://support.microsoft.com/kb/2606883
Ok, ich habe ein Problem, das ich StackOverflow mitteilen wollte, um zu sehen, ob jemand eine Idee hat.
Beachten Sie, dass dies mit SQL Server 2008 R2 möglich ist
Problem: Das Löschen von 3000 Datensätzen aus einer Tabelle mit 15000 Datensätzen dauert 3-4 Minuten, wenn ein Trigger aktiviert ist, und nur 3-5 Sekunden, wenn der Trigger deaktiviert ist.
Tischaufbau
Zwei Tabellen nennen wir Main und Secondary. Sekundär enthält Datensätze von Elementen, die ich löschen möchte. Wenn ich den Löschvorgang durchführe, verbinde ich mich mit der Sekundärtabelle. Vor der Anweisung delete wird ein Prozess ausgeführt, um die Sekundärtabelle mit zu löschenden Datensätzen zu füllen.
Anweisung löschen:
DELETE FROM MAIN
WHERE ID IN (
SELECT Secondary.ValueInt1
FROM Secondary
WHERE SECONDARY.GUID = '9FFD2C8DD3864EA7B78DA22B2ED572D7'
);
Diese Tabelle enthält viele Spalten und ungefähr 14 verschiedene NC-Indizes. Ich habe eine Reihe verschiedener Dinge ausprobiert, bevor ich feststellte, dass der Auslöser das Problem war.
- Seitensperre aktivieren (standardmäßig deaktiviert)
- Manuell erfasste Statistiken
- Deaktivierte automatische Erfassung von Statistiken
- Geprüfter Indexzustand und Fragmentierung
- Löschte den Clustered-Index aus der Tabelle
- Untersuchte den Ausführungsplan (nichts wurde als fehlender Index angezeigt, und die Kosten betrugen 70 Prozent für das tatsächliche Löschen, wobei etwa 28 Prozent für das Zusammenführen der Datensätze anfielen
Löst aus
Die Tabelle enthält drei Trigger (jeweils einen für Einfüge-, Aktualisierungs- und Löschvorgänge). Ich habe den Code für den Löschtrigger geändert, um nur zurückzukehren und dann einen auszuwählen, um zu sehen, wie oft er ausgelöst wird. Es wird während des gesamten Vorgangs (wie erwartet) nur einmal ausgelöst.
ALTER TRIGGER [dbo].[TR_MAIN_RD] ON [dbo].[MAIN]
AFTER DELETE
AS
SELECT 1
RETURN
Zu rekapitulieren
- Bei aktiviertem Trigger dauert die Ausführung der Anweisung 3-4 Minuten
- With Trigger off - Die Ausführung der Anweisung dauert 3-5 Sekunden
Hat jemand eine Idee warum?
Beachten Sie auch, dass Sie diese Architektur nicht ändern, Indizes entfernen usw. als Lösung hinzufügen möchten. Diese Tabelle ist das zentrale Element für einige wichtige Datenoperationen, und wir mussten sie optimieren und optimieren (Indizes, Seitensperren usw.), damit wichtige parallele Operationen ohne Deadlocks ausgeführt werden können.
Hier ist das xml des Ausführungsplans (die Namen wurden geändert, um die Unschuldigen zu schützen)
<?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>
Nun, hier ist die offizielle Antwort von Microsoft ... die meiner Meinung nach ein großer Konstruktionsfehler ist.
14.11.2011 - Offizielle Antwort wurde geändert. Sie verwenden das Transaktionsprotokoll nicht wie zuvor angegeben. Sie verwenden den internen Speicher (Zeilenebene), um die geänderten Daten in zu kopieren. Sie können immer noch nicht feststellen, warum es so lange gedauert hat.
Wir haben beschlossen, anstelle von Löschtriggern anstelle von Löschtriggern zu verwenden.
Der AFTER-Teil des Triggers veranlasst uns, das Transaktionslog zu lesen, nachdem die Löschvorgänge abgeschlossen sind, und die eingefügte / gelöschte Triggertabelle zu erstellen. Hier verbringen wir die meiste Zeit und dies ist beabsichtigt für den NACH-Teil des Auslösers. Der INSTEAD OF-Trigger würde dieses Verhalten beim Durchsuchen des Transaktionsprotokolls und beim Erstellen einer eingefügten / gelöschten Tabelle verhindern. Außerdem wurde festgestellt, dass die Dinge viel schneller sind, wenn wir alle Spalten mit nvarchar (max) löschen. Dies ist sinnvoll, da es sich um LOB-Daten handelt. Weitere Informationen zu In-Row-Daten finden Sie im folgenden Artikel:
http://msdn.microsoft.com/en-us/library/ms189087.aspx
Zusammenfassung: Nach Abschluss des Löschvorgangs muss der Trigger das Transaktionsprotokoll erneut durchsuchen. Anschließend müssen wir eine Tabelle erstellen und einfügen / löschen, was eine stärkere Nutzung des Transaktionsprotokolls und der Zeit erfordert.
Als Aktionsplan schlagen wir zur Zeit Folgendes vor:
quelle
Nach Plan läuft alles richtig. Sie können versuchen, den Löschvorgang als JOIN anstelle eines IN zu schreiben, wodurch Sie einen anderen Plan erhalten.
Ich bin mir jedoch nicht sicher, wie viel das helfen wird. Wenn der Löschvorgang mit den Auslösern in der Tabelle ausgeführt wird, welche Art von Wartezeit besteht für die Sitzung, die den Löschvorgang ausführt?
quelle