Myślałem, że to zostało rozwiązane za pomocą linku poniżej - obejście działa - ale łatka nie. Praca z pomocą techniczną Microsoft w celu rozwiązania.
http://support.microsoft.com/kb/2606883
Ok, więc mam problem, który chciałem wyrzucić do StackOverflow, aby sprawdzić, czy ktoś ma pomysł.
Uwaga: dotyczy to SQL Server 2008 R2
Problem: Usunięcie 3000 rekordów z tabeli zawierającej 15000 rekordów zajmuje 3-4 minuty, gdy wyzwalacz jest włączony i tylko 3-5 sekund, gdy wyzwalacz jest wyłączony.
Konfiguracja stołu
Dwie stoły nazwiemy Main i Secondary. Secondary zawiera rekordy elementów, które chcę usunąć, więc kiedy wykonuję usuwanie, dołączam do tabeli Secondary. Przed instrukcją delete działa proces, który zapełnia tabelę pomocniczą rekordami do usunięcia.
Usuń instrukcję:
DELETE FROM MAIN
WHERE ID IN (
SELECT Secondary.ValueInt1
FROM Secondary
WHERE SECONDARY.GUID = '9FFD2C8DD3864EA7B78DA22B2ED572D7'
);
Ta tabela ma wiele kolumn i około 14 różnych indeksów NC. Próbowałem kilka różnych rzeczy, zanim ustaliłem, że problem jest wyzwalaczem.
- Włącz blokowanie strony (domyślnie wyłączyliśmy)
- Zebrane statystyki ręcznie
- Wyłączone automatyczne gromadzenie statystyk
- Zweryfikowane zdrowie indeksu i fragmentacja
- Usunięto indeks klastrowany z tabeli
- Zbadano plan wykonania (nic nie wskazuje na brakujące indeksy, a koszt wyniósł 70 procent w stosunku do faktycznego usunięcia, a około 28 procent za połączenie / scalenie rekordów
Wyzwalacze
Tabela ma 3 wyzwalacze (po jednym dla operacji wstawiania, aktualizacji i usuwania). Zmodyfikowałem kod wyzwalacza usuwania, aby po prostu powrócił, a następnie wybierz taki, aby zobaczyć, ile razy jest uruchamiany. Wystrzeliwuje tylko jeden raz podczas całej operacji (zgodnie z oczekiwaniami).
ALTER TRIGGER [dbo].[TR_MAIN_RD] ON [dbo].[MAIN]
AFTER DELETE
AS
SELECT 1
RETURN
Przypomnę
- Przy włączonym wyzwalaczu wypełnienie instrukcji zajmuje 3-4 minuty
- Przy wyłączonym wyzwalaczu wykonanie instrukcji zajmuje 3-5 sekund
Czy ktoś ma jakieś pomysły, dlaczego?
Zauważ też - nie chcę zmieniać tej architektury, dodawać usuń indeksy itp. Jako rozwiązanie. Ta tabela jest centralnym elementem niektórych głównych operacji na danych i musieliśmy ją dostosować i dostroić (indeksy, blokowanie stron itp.), Aby umożliwić działanie dużych operacji na współbieżności bez zakleszczeń.
Oto plan wykonania xml (nazwy zostały zmienione, aby chronić niewinnych)
<?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>
Cóż, oto oficjalna odpowiedź Microsoftu ... która moim zdaniem jest poważną wadą projektową.
11/14/2011 - Oficjalna odpowiedź uległa zmianie. Nie używają dziennika transakcji, jak wcześniej wspomniano. Używają magazynu wewnętrznego (poziom wiersza) do kopiowania zmienionych danych. Nadal nie mogą ustalić, dlaczego zajęło to tak długo.
Zdecydowaliśmy się użyć wyzwalaczy zamiast Zamiast zamiast wyzwalaczy po usunięciu.
Część PO wyzwalaczu powoduje, że musimy czytać dziennik transakcji po zakończeniu usuwania i budować tabelę wstawiania / usuwania wyzwalacza. W tym miejscu spędzamy ogromną ilość czasu i jest to zgodne z projektem PO części wyzwalacza. INSTEAD OF wyzwalałby takie zachowanie podczas skanowania dziennika transakcji i budowania wstawionej / usuniętej tabeli. Ponadto, jak zauważono, że rzeczy są znacznie szybsze, jeśli upuszczamy wszystkie kolumny z nvarchar (max), co ma sens ze względu na fakt, że są uważane za dane LOB. Zapoznaj się z poniższym artykułem, aby uzyskać więcej informacji dotyczących danych w wierszu:
http://msdn.microsoft.com/en-us/library/ms189087.aspx
Podsumowanie: PO wyzwalaczu wymaga ponownego skanowania dziennika transakcji po zakończeniu usuwania, wówczas musimy zbudować i wstawić / usunąć tabelę, co wymaga większego wykorzystania dziennika transakcji i czasu.
Tak więc jako plan działania proponujemy obecnie:
źródło
Zgodnie z planem wszystko idzie poprawnie. Możesz spróbować napisać delete jako DOŁĄCZ zamiast IN, co da ci inny plan.
Nie jestem jednak pewien, ile to pomoże. Kiedy usuwanie działa z wyzwalaczami w tabeli, jaki jest typ oczekiwania dla sesji wykonującej usuwanie?
źródło