Frage

Ich dachte, dies wurde mit dem Link unten gelöst - die Arbeiten funktionieren -, aber der Patch nicht. Arbeiten mit Microsoft Support zur Auflösung.

http://support.microsoft.com/kb/2606883

Ok, ich habe ein Problem, das ich zum Stackoverflow rauswerfen wollte, um zu sehen, ob jemand eine Idee hat.

Beachten Sie, dass dies bei SQL Server 2008 R2 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 Auslöser deaktiviert ist.

Tabellenaufbau

Zwei Tische, die wir Main und Secondary nennen. Sekundär enthält Datensätze von Elementen, die ich löschen möchte. Wenn ich den Löschen ausführe, beit mich ich zur Sekundärtabelle aus. Ein Prozess wird vor der Anweisung Löschung ausgeführt, um die sekundäre Tabelle mit zu gelöschten 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 ein paar verschiedene Dinge ausprobiert, bevor ich feststellte, dass der Abzug das Problem war.

  • Einschalten der Seitensperrung (wir haben standardmäßig ausgeschaltet)
  • Statistik manuell gesammelt
  • Behinderte automatische Sammlung von Statistiken
  • Verifizierte Indexgesundheit und Fragmentierung
  • Ließ den Cluster -Index aus der Tabelle fallen
  • Untersuchte den Ausführungsplan (nichts, was als fehlende Indizes und die Kosten für den tatsächlichen Löschen von 70 Prozent mit etwa 28 Prozent für die Join / Merge der Aufzeichnungen lagen

Löst aus

Die Tabelle verfügt über 3 Auslöser (jeweils eine zum Einfügen, Aktualisieren und Löschen von Vorgängen). Ich habe den Code geändert, damit der Löschauslöser einfach zurückkehrt, und dann einen auszuwählen, um zu sehen, wie oft er abgefeuert wird. Es feuert nur einmal während der gesamten Operation (wie erwartet) aus.

ALTER TRIGGER [dbo].[TR_MAIN_RD] ON [dbo].[MAIN]
            AFTER DELETE
            AS  
                SELECT 1
                RETURN

Zusammenfassen

  • Mit Auslöser eins - Die Anweisung dauert 3-4 Minuten, bis es fertig ist
  • Mit Auslöser - Die Anweisung dauert 3-5 Sekunden, bis er abgeschlossen ist

Hat jemand irgendwelche Ideen, warum?

HINWEIS - Ich möchte diese Architektur nicht ändern, Entfernen von Indizes usw. als Lösung hinzufügen. Diese Tabelle ist das Zentrum für einige wichtige Datenoperationen, und wir mussten sie optimieren und einstellen (Indizes, Seitenverriegelung usw.), damit große Parallelitätsvorgänge ohne Deadlocks funktionieren können.

Hier ist der Ausführungsplan XML (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>
War es hilfreich?

Lösung 2

Nun, hier ist die offizielle Antwort von Microsoft ... was meiner Meinung nach ein großer Designfehler ist.

14.11.2011 - Die offizielle Antwort hat sich geändert. Sie verwenden das Transaktionsprotokoll nicht wie zuvor angegeben. Die internen Store (Zeilenebene) verwenden die geänderten Daten in. Sie können immer noch nicht bestimmen, warum es so lange gedauert hat.

Wir haben uns entschlossen, anstelle von Auslöser anstelle von nach dem Löschen von Triggern zu verwenden.

Durch den After -Teil des Auslösers müssen wir das Transaktionsprotokoll durchlesen, nachdem die Deletten abgeschlossen und den eingelagerten/gelöschten Abzug erstellt wurden. Hier verbringen wir die enge Zeit und sind mit dem Design für den After -Teil des Abzugs entworfen. Anstelle des Triggers verhindern dieses Verhalten des Scannens des Transaktionsprotokolls und des Erstellens einer eingefügten/gelöschten Tabelle. Wie beobachtet wurde, dass die Dinge viel schneller sind, wenn wir alle Spalten mit Nvarchar (max) fallen lassen, was aufgrund der Tatsache, dass sie als LOB -Daten betrachtet werden, sinnvoll sind. Bitte haben Sie ALOOK im folgenden Artikel, um weitere Informationen zu In-Reihe-Daten zu erhalten:

http://msdn.microsoft.com/en-us/library/ms189087.aspx

Zusammenfassung: Nach dem Auslöser müssen nach den Löschvorschlägen das Transaktionsprotokoll durch das Durchsuchen des Transaktionsprotokolls erstellt und Tabelle eingefügt/gelöscht werden, für die mehr Einsatz des Transaktionsprotokolls und der Zeit erforderlich ist.

Als Aktionsplan empfehlen wir dies zu diesem Zeitpunkt:

A) Limit the number of rows deleted in each transaction or
B) Increase timeout settings or
C) Don't use AFTER trigger or trigger at all or
D) Limit usage of nvarchar(max) datatypes.

Andere Tipps

Das in SQL Server 2005 eingeführte Rahmenversionsframework wird verwendet, um eine Reihe von Funktionen zu unterstützen, einschließlich der neuen Transaktions-Isolationsstufen READ_COMMITTED_SNAPSHOT und SNAPSHOT. Selbst wenn keiner dieser Isolationsniveaus aktiviert ist AFTER Auslöser (um die Erzeugung der Erzeugung zu erleichtern inserted und deleted Pseudo-Tabellen), Mars und (in einem separaten Versionsspeicher) Online-Indexierung.

Wie dokumentiert, Der Motor kann jeder Zeile einer Tabelle, die für jeden dieser Zwecke versioniert wird, ein 14-Byte-Postfix hinzufügen. Dieses Verhalten ist relativ bekannt, ebenso wie die Zugabe der 14-Byte-Daten zu jeder Zeile eines Index, das ist online umgebaut mit einer zeilenversionen-Isolationsebene aktiviert. Selbst wenn die Isolationsniveaus nicht aktiviert sind, wird ein zusätzliches Byte hinzugefügt zu hinzugefügt NUR nicht geklustere Indizes Beim Wiederaufbau ONLINE.

Wenn ein After -Trigger vorhanden ist und die Versionierung sonst 14 Bytes pro Zeile hinzufügen würde, besteht eine Optimierung innerhalb des Motors zu vermeiden das, aber wo a ROW_OVERFLOW oder LOB Die Zuteilung kann nicht auftreten. In der Praxis bedeutet dies, dass die maximal mögliche Größe einer Reihe weniger als 8060 Bytes betragen muss. Bei der Berechnung maximal Mögliche Zeilengrößen nimmt der Motor beispielsweise an, dass eine Varchar (460) -Säule 460 Zeichen enthalten könnte.

Das Verhalten ist am einfachsten mit einem zu sehen AFTER UPDATE Auslöser, obwohl das gleiche Prinzip gilt für AFTER DELETE. Das folgende Skript erstellt eine Tabelle mit einer maximalen In-Reihe-Länge von 8060 Bytes. Die Daten passt auf eine einzelne Seite mit 13 Bytes freier Speicherplatz auf dieser Seite. Es gibt einen Auslöser ohne OP-OP, sodass die Seite geteilt und Versionsinformationen hinzugefügt werden:

USE Sandpit;
GO
CREATE TABLE dbo.Example
(
    ID          integer NOT NULL IDENTITY(1,1),
    Value       integer NOT NULL,
    Padding1    char(42) NULL,
    Padding2    varchar(8000) NULL,

    CONSTRAINT PK_Example_ID
    PRIMARY KEY CLUSTERED (ID)
);
GO
WITH
    N1 AS (SELECT 1 AS n UNION ALL SELECT 1),
    N2 AS (SELECT L.n FROM N1 AS L CROSS JOIN N1 AS R),
    N3 AS (SELECT L.n FROM N2 AS L CROSS JOIN N2 AS R),
    N4 AS (SELECT L.n FROM N3 AS L CROSS JOIN N3 AS R)
INSERT TOP (137) dbo.Example
    (Value)
SELECT
    ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM N4;
GO
ALTER INDEX PK_Example_ID 
ON dbo.Example 
REBUILD WITH (FILLFACTOR = 100);
GO
SELECT
    ddips.index_type_desc,
    ddips.alloc_unit_type_desc,
    ddips.index_level,
    ddips.page_count,
    ddips.record_count,
    ddips.max_record_size_in_bytes
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(N'dbo.Example', N'U'), 1, 1, 'DETAILED') AS ddips
WHERE
    ddips.index_level = 0;
GO
CREATE TRIGGER ExampleTrigger
ON dbo.Example
AFTER DELETE, UPDATE
AS RETURN;
GO
UPDATE dbo.Example
SET Value = -Value
WHERE ID = 1;
GO
SELECT
    ddips.index_type_desc,
    ddips.alloc_unit_type_desc,
    ddips.index_level,
    ddips.page_count,
    ddips.record_count,
    ddips.max_record_size_in_bytes
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(N'dbo.Example', N'U'), 1, 1, 'DETAILED') AS ddips
WHERE
    ddips.index_level = 0;
GO
DROP TABLE dbo.Example;

Das Drehbuch erzeugt die unten gezeigte Ausgabe. Die einseitige Tabelle ist in zwei Seiten und das Maximum aufgeteilt physisch Die Zeilenlänge hat sich von 57 auf 71 Bytes erhöht (= +14 Bytes für die Informationen zur Zeilenversorgung).

Update example

DBCC PAGE zeigt, dass die einzelne aktualisierte Zeile hat Record Attributes = NULL_BITMAP VERSIONING_INFO Record Size = 71, während alle anderen Reihen in der Tabelle haben Record Attributes = NULL_BITMAP; record Size = 57.

Das gleiche Skript mit dem UPDATE ersetzt durch eine einzelne Reihe DELETE Erzeugt die gezeigte Ausgabe:

DELETE dbo.Example
WHERE ID = 1;

Delete example

Es gibt insgesamt eine Reihe weniger (natürlich!), Aber die maximale physische Reihengröße hat nicht erhöht. Zeilenversionsinformationen werden nur zu den Zeilen hinzugefügt, die für die Trigger-Pseudo-Tabellen benötigt werden, und diese Zeile wurde letztendlich gelöscht. Die Seitenaufteilung bleibt jedoch bestehen. Diese auf Seite spaltende Aktivität ist für die langsame Leistung verantwortlich, die beim Bestehen des Auslösers beobachtet wird. Wenn die Definition der Padding2 Die Spalte wird von geändert von varchar(8000) zu varchar(7999), Die Seite spaltet nicht mehr auf.

Sehen Sie auch das Blogeintrag von SQL Server MVP Dmitri Korotkevitch, in dem auch die Auswirkungen auf die Fragmentierung erörtert werden.

Nach dem Plan geht alles richtig. Sie können versuchen, den Löschen als Join anstelle eines Ins zu schreiben, das Ihnen einen anderen Plan bietet.

DELETE m
FROM MAIN m
JOIN Secondary s ON m.ID = s.ValueInt1
AND s.SetTMGUID = '9DDD2C8DD3864EA7B78DA22B2ED572D7'

Ich bin mir jedoch nicht sicher, wie viel das helfen wird. Wenn der Löschen mit den Triggern auf der Tabelle ausgeführt wird, was ist der Wartentyp für die Sitzung, die das Löschen durchführt?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit dba.stackexchange
scroll top