Dieser Beitrag enthält neue Informationen über die Voraussetzungen für minimal protokolliertes Massenladen bei Verwendung von INSERT...SELECT
in indizierte Tabellen .
Die interne Einrichtung, die diese Fälle ermöglicht, heißt FastLoadContext
. Es kann von SQL Server 2008 bis einschließlich 2014 mithilfe des dokumentierten Ablaufverfolgungsflags 610 aktiviert werden. Ab SQL Server 2016 FastLoadContext
ist standardmäßig aktiviert; das Trace-Flag ist nicht erforderlich.
Ohne FastLoadContext
, die einzigen Indexeinfügungen, die minimal protokolliert werden können sind die in ein leeres gruppierter Index ohne Sekundärindizes, wie in Teil 2 dieser Serie behandelt. Die minimale Protokollierung Bedingungen für nicht indizierte Heap-Tabellen wurden in Teil eins behandelt.
Weitere Hintergrundinformationen finden Sie im Data Performance Loading Guide und beim Tiger Team Hinweise zu den Verhaltensänderungen für SQL Server 2016.
Kontext für schnelles Laden
Zur Erinnerung:RowsetBulk
-Einrichtung (in Teil 1 und 2 behandelt) aktiviert minimal protokolliert Massenladen für:
- Leerer und nicht leerer Haufen Tabellen mit:
- Tabellensperre; und
- Keine sekundären Indizes.
- Leere geclusterte Tabellen , mit:
- Tabellensperre; und
- Keine sekundären Indizes; und
DMLRequestSort=true
auf der Clustered Index Insert Betreiber.
Der FastLoadContext
Codepfad fügt Unterstützung für minimal protokolliert hinzu und gleichzeitig Massenladen am:
- Leer und nicht leer geclustert B-Tree-Indizes.
- Leer und nicht leer nicht gruppiert B-Tree-Indizes, die von einem dedizierten verwaltet werden Index einfügen Planbetreiber.
Der FastLoadContext
erfordert auch DMLRequestSort=true
in jedem Fall beim entsprechenden Tarifbetreiber.
Möglicherweise haben Sie eine Überschneidung zwischen RowsetBulk
bemerkt und FastLoadContext
für leere gruppierte Tabellen ohne Sekundärindizes. Ein TABLOCK
Hinweis ist nicht erforderlich mit FastLoadContext
, muss aber nicht fehlen entweder. Als Konsequenz eine passende Einfügung mit TABLOCK
kann immer noch für minimale Protokollierung in Frage kommen über FastLoadContext
wenn es fehlschlägt, den detaillierten RowsetBulk
Tests.
FastLoadContext
kann deaktiviert werden auf SQL Server 2016 unter Verwendung des dokumentierten Ablaufverfolgungsflags 692. Das erweiterte Ereignis des Debug-Kanals fastloadcontext_enabled
kann verwendet werden, um FastLoadContext
zu überwachen Nutzung pro Indexpartition (Rowset). Dieses Ereignis wird nicht für RowsetBulk
ausgelöst lädt.
Gemischte Protokollierung
Ein einzelnes INSERT...SELECT
Anweisung mit FastLoadContext
kann sich vollständig anmelden einige Zeilen bei minimaler Protokollierung andere.
Zeilen werden eine nach der anderen eingefügt durch die Indexeinlage Bediener und vollständig protokolliert in den folgenden Fällen:
- Alle Zeilen, die der ersten hinzugefügt wurden Indexseite, wenn der Index leer war zu Beginn der Operation.
- Zeilen zu bestehenden hinzugefügt Indexseiten.
- Zeilen verschoben zwischen den Seiten durch eine Seitenteilung.
Andernfalls werden Zeilen aus dem bestellten Insert-Stream zu einer brandneuen Seite hinzugefügt mit einem optimierten und minimal protokollierten Codepfad. Sobald so viele Zeilen wie möglich auf die neue Seite geschrieben wurden, wird sie direkt in die bestehende Zielindexstruktur verlinkt.
Die neu hinzugefügte Seite wird nicht unbedingt voll sein (obwohl das offensichtlich der Idealfall ist), da SQL Server darauf achten muss, der neuen Seite keine Zeilen hinzuzufügen, die logisch zu einer vorhandenen gehören Indexseite. Die neue Seite wird als Einheit in den Index „eingefügt“, sodass wir keine Zeilen auf der neuen Seite haben können, die woanders hingehören. Dies ist hauptsächlich ein Problem, wenn Zeilen innerhalb hinzugefügt werden dem vorhandenen Schlüsselbereich des Index, und nicht vor dem Start oder nach dem Ende des vorhandenen Indexschlüsselbereichs.
Es ist immer noch möglich um neue Seiten innerhalb hinzuzufügen der vorhandene Indexschlüsselbereich, aber die neuen Zeilen müssen höher sortiert werden als der höchste Schlüssel auf dem vorhergehenden vorhandene Indexseite und sortieren Sie niedriger als den niedrigsten Schlüssel auf dem folgenden vorhandene Indexseite. Für die beste Chance, eine minimale Protokollierung zu erreichen Stellen Sie unter diesen Umständen sicher, dass sich die eingefügten Zeilen so weit wie möglich nicht mit vorhandenen Zeilen überschneiden.
DMLRequestSort-Bedingungen
Denken Sie an FastLoadContext
kann nur aktiviert werden, wenn DMLRequestSort
auf true gesetzt ist für die entsprechende Indexeinlage Operator im Ausführungsplan.
Es gibt zwei Hauptcodepfade, die DMLRequestSort
festlegen können auf wahr für Indexeinsätze. Beide Wege gibt wahr zurück ist ausreichend.
1. FOptimizeInsert
Das sqllang!CUpdUtil::FOptimizeInsert
Code erfordert:
- Mehr als 250 Zeilen geschätzt eingefügt werden; und
- Mehr als 2 Seiten geschätzt Datengröße einfügen; und
- Der Zielindex muss weniger als 3 Blattseiten haben .
Diese Bedingungen sind dieselben wie bei RowsetBulk
auf einem leeren gruppierten Index, mit einer zusätzlichen Anforderung von nicht mehr als zwei Seiten auf Indexblattebene. Beachten Sie sorgfältig, dass sich dies auf die Größe des vorhandenen Indexes bezieht vor der Einfügung, nicht die geschätzte Größe der hinzuzufügenden Daten.
Das folgende Skript ist eine Modifikation der Demo, die in früheren Teilen dieser Serie verwendet wurde. Es zeigt minimale Protokollierung wenn vorher weniger als drei Indexseiten gefüllt sind der Test INSERT...SELECT
läuft. Das Testtabellenschema ist so ausgelegt, dass 130 Zeilen auf eine einzelne 8-KB-Seite passen, wenn die Zeilenversionierung für die Datenbank deaktiviert ist. Der Multiplikator im ersten TOP
-Klausel kann geändert werden, um die Anzahl der vorhandenen Indexseiten vorher zu bestimmen der Test INSERT...SELECT
wird ausgeführt:
IF OBJECT_ID(N'dbo.Test', N'U') IS NOT NULL BEGIN DROP TABLE dbo.Test; END; GO CREATE TABLE dbo.Test ( id integer NOT NULL IDENTITY CONSTRAINT [PK dbo.Test (id)] PRIMARY KEY, c1 integer NOT NULL, padding char(45) NOT NULL DEFAULT '' ); GO -- 130 rows per page for this table -- structure with row versioning off INSERT dbo.Test (c1) SELECT TOP (3 * 130) -- Change the 3 here CHECKSUM(NEWID()) FROM master.dbo.spt_values AS SV; GO -- Show physical index statistics -- to confirm the number of pages SELECT DDIPS.index_type_desc, DDIPS.alloc_unit_type_desc, DDIPS.page_count, DDIPS.record_count, DDIPS.avg_record_size_in_bytes FROM sys.dm_db_index_physical_stats ( DB_ID(), OBJECT_ID(N'dbo.Test', N'U'), 1, -- Index ID NULL, -- Partition ID 'DETAILED' ) AS DDIPS WHERE DDIPS.index_level = 0; -- leaf level only GO -- Clear the plan cache DBCC FREEPROCCACHE; GO -- Clear the log CHECKPOINT; GO -- Main test INSERT dbo.Test (c1) SELECT TOP (269) CHECKSUM(NEWID()) FROM master.dbo.spt_values AS SV; GO -- Show log entries SELECT FD.Operation, FD.Context, FD.[Log Record Length], FD.[Log Reserve], FD.AllocUnitName, FD.[Transaction Name], FD.[Lock Information], FD.[Description] FROM sys.fn_dblog(NULL, NULL) AS FD; GO -- Count the number of fully-logged rows SELECT [Fully Logged Rows] = COUNT_BIG(*) FROM sys.fn_dblog(NULL, NULL) AS FD WHERE FD.Operation = N'LOP_INSERT_ROWS' AND FD.Context = N'LCX_CLUSTERED' AND FD.AllocUnitName = N'dbo.Test.PK dbo.Test (id)'; GO
Wenn der gruppierte Index mit 3 Seiten vorab geladen wird , wird die Testeinlage vollständig protokolliert (Detailaufzeichnungen des Transaktionsprotokolls wurden der Kürze halber weggelassen):
Wenn die Tabelle vorab mit nur 1 oder 2 Seiten geladen ist , der Testeinsatz ist minimal protokolliert :
Wenn die Tabelle nicht vorab geladen ist Mit beliebigen Seiten entspricht der Test dem Ausführen der leeren gruppierten Tabellendemo aus Teil zwei, jedoch ohne den TABLOCK
Hinweis:
Die ersten 130 Zeilen werden vollständig protokolliert . Das liegt daran, dass der Index leer war, bevor wir angefangen haben, und 130 Zeilen auf die erste Seite passen. Denken Sie daran, dass die erste Seite immer vollständig protokolliert wird, wenn FastLoadContext
verwendet wird und der Index vorher leer war. Die restlichen 139 Zeilen werden mit minimaler Protokollierung eingefügt .
Wenn ein TABLOCK
Der Einfügung wird ein Hinweis hinzugefügt, alle Seiten werden minimal protokolliert (einschließlich des ersten), da das Laden des leeren Clustered-Index nun für den RowsetBulk
qualifiziert Mechanismus (auf Kosten eines Sch-M
sperren).
2. FDemandRowsSortedForPerformance
Wenn der FOptimizeInsert
Tests schlagen fehl, DMLRequestSort
kann immer noch auf true gesetzt werden durch eine zweite Reihe von Tests in sqllang!CUpdUtil::FDemandRowsSortedForPerformance
Code. Diese Bedingungen sind etwas komplexer, daher ist es sinnvoll, einige Parameter zu definieren:
P
– Anzahl der vorhandenen Seiten auf Blattebene im Zielindex .I
– geschätzt Anzahl der einzufügenden Zeilen.R
=P
/I
(Zielseiten pro eingefügter Zeile).T
– Anzahl der Zielpartitionen (1 für unpartitioniert).
Die Logik zur Bestimmung des Werts von DMLRequestSort
ist dann:
- Wenn
P <= 16
false zurückgeben , sonst :- Wenn
R < 8
:- Wenn
P > 524
true zurückgeben , andernfalls false .
- Wenn
- Wenn
R >= 8
:- Wenn
T > 1
undI > 250
true zurückgeben , andernfalls false .
- Wenn
- Wenn
Die obigen Tests werden vom Abfrageprozessor während der Plankompilierung ausgewertet. Es gibt eine Endbedingung vom Speicher-Engine-Code ausgewertet (IndexDataSetSession::WakeUpInternal
) zur Ausführungszeit:
DMLRequestSort
ist derzeit wahr; undI >= 100
.
Als nächstes werden wir all diese Logik in überschaubare Teile zerlegen.
Mehr als 16 vorhandene Zielseiten
Der erste Test P <= 16
bedeutet, dass Indizes mit weniger als 17 vorhandenen Blattseiten nicht für FastLoadContext
qualifiziert sind über diesen Codepfad. Um das ganz klar zu sagen, P
ist die Anzahl der Seiten auf Blattebene im Zielindex vorher das INSERT...SELECT
wird ausgeführt.
Um diesen Teil der Logik zu demonstrieren, werden wir die geclusterte Testtabelle mit 16 Seiten vorab laden von Dateien. Dies hat zwei wichtige Auswirkungen (denken Sie daran, dass beide Codepfade false zurückgeben müssen um mit einem false zu enden Wert für DMLRequestSort
):
- Es stellt sicher, dass das vorherige
FOptimizeInsert
Test schlägt fehl , weil die dritte Bedingung nicht erfüllt ist (P < 3
). - Der
P <= 16
Bedingung inFDemandRowsSortedForPerformance
auch nicht erfüllt werden.
Wir erwarten daher FastLoadContext
nicht freigeschaltet werden. Das modifizierte Demoskript lautet:
IF OBJECT_ID(N'dbo.Test', N'U') IS NOT NULL BEGIN DROP TABLE dbo.Test; END; GO CREATE TABLE dbo.Test ( id integer NOT NULL IDENTITY CONSTRAINT [PK dbo.Test (id)] PRIMARY KEY, c1 integer NOT NULL, padding char(45) NOT NULL DEFAULT '' ); GO -- 130 rows per page for this table -- structure with row versioning off INSERT dbo.Test (c1) SELECT TOP (16 * 130) -- 16 pages CHECKSUM(NEWID()) FROM master.dbo.spt_values AS SV; GO -- Show physical index statistics -- to confirm the number of pages SELECT DDIPS.index_type_desc, DDIPS.alloc_unit_type_desc, DDIPS.page_count, DDIPS.record_count, DDIPS.avg_record_size_in_bytes FROM sys.dm_db_index_physical_stats ( DB_ID(), OBJECT_ID(N'dbo.Test', N'U'), 1, -- Index ID NULL, -- Partition ID 'DETAILED' ) AS DDIPS WHERE DDIPS.index_level = 0; -- leaf level only GO -- Clear the plan cache DBCC FREEPROCCACHE; GO -- Clear the log CHECKPOINT; GO -- Main test INSERT dbo.Test (c1) SELECT TOP (269) CHECKSUM(NEWID()) FROM master.dbo.spt_values AS SV1 CROSS JOIN master.dbo.spt_values AS SV2; GO -- Show log entries SELECT FD.Operation, FD.Context, FD.[Log Record Length], FD.[Log Reserve], FD.AllocUnitName, FD.[Transaction Name], FD.[Lock Information], FD.[Description] FROM sys.fn_dblog(NULL, NULL) AS FD; GO -- Count the number of fully-logged rows SELECT [Fully Logged Rows] = COUNT_BIG(*) FROM sys.fn_dblog(NULL, NULL) AS FD WHERE FD.Operation = N'LOP_INSERT_ROWS' AND FD.Context = N'LCX_CLUSTERED' AND FD.AllocUnitName = N'dbo.Test.PK dbo.Test (id)';
Alle 269 Zeilen werden vollständig protokolliert wie vorhergesagt:
Beachten Sie, dass das obige Skript nie wird, egal wie hoch wir die Anzahl der einzufügenden neuen Zeilen festlegen erzeugen minimale Protokollierung wegen P <= 16
test (und P < 3
in FOptimizeInsert
testen ).
Wenn Sie die Demo selbst mit einer größeren Anzahl von Zeilen ausführen möchten, kommentieren Sie den Abschnitt aus, in dem einzelne Transaktionsprotokolleinträge angezeigt werden. Andernfalls warten Sie sehr lange und SSMS kann abstürzen. (Um fair zu sein, könnte es das trotzdem tun, aber warum das Risiko erhöhen.)
Verhältnis Seiten pro eingefügter Zeile
Wenn es 17 oder mehr sind Blattseiten im bestehenden Index, die bisherigen P <= 16
Test wird nicht fehlschlagen. Der nächste Abschnitt der Logik befasst sich mit dem Verhältnis von vorhandenen Seiten zu neu eingefügten Zeilen . Dies muss auch bestanden werden, um eine minimale Protokollierung zu erreichen . Zur Erinnerung, die relevanten Bedingungen sind:
- Verhältnis
R
=P
/I
. - Wenn
R < 8
:- Wenn
P > 524
true zurückgeben , andernfalls false .
- Wenn
Wir müssen uns auch an den abschließenden Speicher-Engine-Test für mindestens 100 Zeilen erinnern:
I >= 100
.
Diese Bedingungen ein bisschen neu organisieren, alle Folgendes muss zutreffen:
P > 524
(vorhandene Indexseiten)I >= 100
(geschätzte eingefügte Zeilen)P / I < 8
(VerhältnisR
)
Es gibt mehrere Möglichkeiten, diese drei Bedingungen gleichzeitig zu erfüllen. Wählen wir die minimal möglichen Werte für P
(525) und I
(100) gibt ein R
Wert von (525/100) =5,25. Dies erfüllt die (R < 8
test), also erwarten wir, dass diese Kombination zu minimaler Protokollierung führt :
IF OBJECT_ID(N'dbo.Test', N'U') IS NOT NULL BEGIN DROP TABLE dbo.Test; END; GO CREATE TABLE dbo.Test ( id integer NOT NULL IDENTITY CONSTRAINT [PK dbo.Test (id)] PRIMARY KEY, c1 integer NOT NULL, padding char(45) NOT NULL DEFAULT '' ); GO -- 130 rows per page for this table -- structure with row versioning off INSERT dbo.Test (c1) SELECT TOP (525 * 130) -- 525 pages CHECKSUM(NEWID()) FROM master.dbo.spt_values AS SV1 CROSS JOIN master.dbo.spt_values AS SV2; GO -- Show physical index statistics -- to confirm the number of pages SELECT DDIPS.index_type_desc, DDIPS.alloc_unit_type_desc, DDIPS.page_count, DDIPS.record_count, DDIPS.avg_record_size_in_bytes FROM sys.dm_db_index_physical_stats ( DB_ID(), OBJECT_ID(N'dbo.Test', N'U'), 1, -- Index ID NULL, -- Partition ID 'DETAILED' ) AS DDIPS WHERE DDIPS.index_level = 0; -- leaf level only GO -- Clear the plan cache DBCC FREEPROCCACHE; GO -- Clear the log CHECKPOINT; GO -- Main test INSERT dbo.Test (c1) SELECT TOP (100) CHECKSUM(NEWID()) FROM master.dbo.spt_values AS SV1 CROSS JOIN master.dbo.spt_values AS SV2; GO -- Show log entries SELECT FD.Operation, FD.Context, FD.[Log Record Length], FD.[Log Reserve], FD.AllocUnitName, FD.[Transaction Name], FD.[Lock Information], FD.[Description] FROM sys.fn_dblog(NULL, NULL) AS FD; GO -- Count the number of fully-logged rows SELECT [Fully Logged Rows] = COUNT_BIG(*) FROM sys.fn_dblog(NULL, NULL) AS FD WHERE FD.Operation = N'LOP_INSERT_ROWS' AND FD.Context = N'LCX_CLUSTERED' AND FD.AllocUnitName = N'dbo.Test.PK dbo.Test (id)';
Das 100-Zeilen-INSERT...SELECT
tatsächlich minimal protokolliert :
Reduzierung der geschätzten Eingefügte Zeilen bis 99 (Breaking I >= 100
) und/oder die Anzahl der vorhandenen Indexseiten auf 524 reduzieren (brechen von P > 524
) führt zu einer vollständigen Protokollierung . Wir könnten auch Änderungen wie R
vornehmen ist nicht länger kleiner als 8, um vollständige Protokollierung zu erzeugen . Setzen Sie beispielsweise P = 1000
und I = 125
ergibt R = 8
, mit den folgenden Ergebnissen:
Die 125 eingefügten Zeilen wurden vollständig protokolliert wie erwartet. (Dies liegt nicht an der vollen Protokollierung der ersten Seite, da der Index vorher nicht leer war.)
Seitenverhältnis für partitionierte Indizes
Wenn alle vorhergehenden Tests fehlschlagen, erfordert der verbleibende Test R >= 8
und kann nur zufrieden sein, wenn die Anzahl der Partitionen (T
) ist größer als 1 und geschätzt sind es mehr als 250 eingefügte Zeilen (I
). Rückruf:
- Wenn
R >= 8
:- Wenn
T > 1
undI > 250
true zurückgeben , andernfalls false .
- Wenn
Eine Feinheit:Für partitioniert Indizes gilt die Regel, dass alle Zeilen der ersten Seite vollständig protokolliert werden (für einen anfänglich leeren Index), pro Partition . Für ein Objekt mit 15.000 Partitionen bedeutet das 15.000 vollständig protokollierte „erste“ Seiten.
Zusammenfassung und abschließende Gedanken
Die im Hauptteil beschriebenen Formeln und die Reihenfolge der Auswertung basieren auf der Codeinspektion mit einem Debugger. Sie wurden in einer Form präsentiert, die das Timing und die Reihenfolge, die im echten Code verwendet werden, genau wiedergibt.
Es ist möglich, diese Bedingungen ein wenig neu zu ordnen und zu vereinfachen, um eine präzisere Zusammenfassung der praktischen Anforderungen für minimale Protokollierung zu erhalten beim Einfügen in einen B-Baum mit INSERT...SELECT
. Die folgenden verfeinerten Ausdrücke verwenden die folgenden drei Parameter:
P
=Anzahl der vorhandenen Seiten auf Blattebene indexieren.I
=geschätzt Anzahl der einzufügenden Zeilen.S
=geschätzt Datengröße in 8-KB-Seiten einfügen.
Rowset-Massenladen
- Verwendet
sqlmin!RowsetBulk
. - Erfordert ein leeres gruppiertes Indexziel mit
TABLOCK
(oder gleichwertig). - Benötigt
DMLRequestSort = true
auf der Clustered Index Insert Betreiber. DMLRequestSort
auftrue
gesetzt ist wennI > 250
undS > 2
.- Alle eingefügten Zeilen werden minimal protokolliert .
- Ein
Sch-M
Sperre verhindert gleichzeitigen Zugriff auf Tabellen.
Kontext für schnelles Laden
- Verwendet
sqlmin!FastLoadContext
. - Aktiviert minimal protokolliert fügt in B-Tree-Indizes ein:
- Geclustert oder nicht geclustert.
- Mit oder ohne Tabellensperre.
- Zielindex leer oder nicht.
- Benötigt
DMLRequestSort = true
auf dem zugehörigen Index Insert Planbetreiber. - Nur Zeilen, die auf ganz neue Seiten geschrieben werden werden in großen Mengen geladen und minimal protokolliert .
- Die erste Seite eines zuvor leeren Index Partition wird immer vollständig protokolliert .
- Absolutes Minimum von
I >= 100
. - Erfordert Trace-Flag 610 vor SQL Server 2016.
- Standardmäßig ab SQL Server 2016 verfügbar (Trace-Flag 692 deaktiviert).
DMLRequestSort
auf true
gesetzt ist für:
- Jeder Index (partitioniert oder nicht) if:
I > 250
undP < 3
undS > 2
; oderI >= 100
undP > 524
undP < I * 8
Nur für partitionierte Indizes (bei> 1 Partition), DMLRequestSort
wird ebenfalls auf true
gesetzt wenn:
I > 250
undP > 16
undP >= I * 8
Es gibt ein paar interessante Fälle, die sich aus diesen FastLoadContext
ergeben Bedingungen:
- Alle Einfügungen in eine nicht partitionierte Index mit zwischen 3 und 524 (inklusive) vorhandene Blattseiten werden vollständig protokolliert unabhängig von der Anzahl und Gesamtgröße der hinzugefügten Zeilen. Dies wirkt sich am deutlichsten auf große Einfügungen in kleine (aber nicht leere) Tabellen aus.
- Alle Einfügungen in eine partitionierte Index mit zwischen 3 und 16 vorhandene Seiten werden vollständig protokolliert .
- Große Einsätze zu groß nicht partitioniert Indizes dürfen nicht minimal protokolliert werden aufgrund der Ungleichung
P < I * 8
. WennP
groß ist, wird ein entsprechend großer geschätzt Anzahl der eingefügten Zeilen (I
) erforderlich. Beispielsweise kann ein Index mit 8 Millionen Seiten die minimale Protokollierung nicht unterstützen beim Einfügen von 1 Million Zeilen oder weniger.
Nicht gruppierte Indizes
Die gleichen Überlegungen und Berechnungen, die in den Demos für geclusterte Indizes angewendet wurden, gelten für nonclustered B-Tree-Indizes ebenfalls, solange der Index von einem dedizierten Planbetreiber verwaltet wird (eine breite , oder pro Index planen). Nonclustered-Indizes, die von einem Basistabellenoperator verwaltet werden (z. B. Clustered Index Insert ) sind nicht für FastLoadContext
geeignet .
Beachten Sie, dass die Formelparameter für jedes nonclustered neu ausgewertet werden müssen Indexoperator – berechnete Zeilengröße, Anzahl vorhandener Indexseiten und Kardinalitätsschätzung.
Allgemeine Bemerkungen
Achten Sie auf niedrige Kardinalitätsschätzungen in der Indexeinlage Operator, da diese das I
beeinflussen und S
Parameter. Wenn ein Schwellenwert aufgrund eines Kardinalitätsschätzungsfehlers nicht erreicht wird, wird die Einfügung vollständig protokolliert .
Denken Sie daran, dass DMLRequestSort
wird mit dem Plan zwischengespeichert — es wird nicht bei jeder Ausführung eines wiederverwendeten Plans ausgewertet. Dies kann eine Form des bekannten Parameter-Sensitivity-Problems (auch bekannt als „Parameter-Sniffing“) einführen.
Der Wert von P
(Indexblattseiten) wird nicht aktualisiert am Anfang jeder Aussage. Die aktuelle Implementierung speichert den Wert für den gesamten Batch . Dies kann unerwartete Nebenwirkungen haben. Zum Beispiel eine TRUNCATE TABLE
in der gleichen Charge als INSERT...SELECT
setzt P
nicht zurück für die in diesem Artikel beschriebenen Berechnungen auf null setzen – sie werden weiterhin den vorab abgeschnittenen Wert verwenden, und eine Neukompilierung wird nicht helfen. Eine Problemumgehung besteht darin, große Änderungen in separaten Stapeln einzureichen.
Trace-Flags
Es ist möglich, FDemandRowsSortedForPerformance
zu erzwingen um true zurückzugeben durch die Einstellung undokumentiert und nicht unterstützt Trace-Flag 2332, wie ich in Optimieren von T-SQL-Abfragen geschrieben habe, die Daten ändern. Wenn TF 2332 aktiv ist, die geschätzte Anzahl einzufügender Zeilen muss trotzdem mindestens 100 sein . TF 2332 beeinflusst die minimale Protokollierung Entscheidung für FastLoadContext
nur (es gilt für partitionierte Heaps bis zu DMLRequestSort
betrifft, hat aber keine Auswirkung auf den Heap selbst, da FastLoadContext
gilt nur für Indizes).
Ein wide/per-index plan shape for nonclustered index maintenance kann für Rowstore-Tabellen mit Ablaufverfolgungs-Flag 8790 erzwungen werden (nicht offiziell dokumentiert, aber in einem Knowledge Base-Artikel sowie in meinem Artikel erwähnt, der für TF2332 oben verlinkt ist).
Verwandte Lektüre
Alles von Sunil Agarwal vom SQL Server-Team:
- Was sind die Massenimportoptimierungen?
- Bulk-Import-Optimierungen (minimale Protokollierung)
- Minimale Protokollierungsänderungen in SQL Server 2008
- Minimale Protokollierungsänderungen in SQL Server 2008 (Teil 2)
- Minimale Protokollierungsänderungen in SQL Server 2008 (Teil 3)