Ich teste minimale Protokolleinfügungen in verschiedenen Szenarien und von dem, was ich unter Verwendung von TABLOCK und SQL Server 2016+ als INSERT INTO SELECT in einen Heap mit einem nicht gruppierten Index gelesen habe, sollte nur eine minimale Protokollierung erfolgen vollständige Protokollierung. Meine Datenbank befindet sich im einfachen Wiederherstellungsmodell und ich erhalte erfolgreich minimal protokollierte Einfügungen auf einem Heap ohne Indizes und TABLOCK.
Ich verwende eine alte Sicherung der Stack Overflow-Datenbank zum Testen und habe ein Replikat der Posts-Tabelle mit dem folgenden Schema erstellt ...
CREATE TABLE [dbo].[PostsDestination](
[Id] [int] NOT NULL,
[AcceptedAnswerId] [int] NULL,
[AnswerCount] [int] NULL,
[Body] [nvarchar](max) NOT NULL,
[ClosedDate] [datetime] NULL,
[CommentCount] [int] NULL,
[CommunityOwnedDate] [datetime] NULL,
[CreationDate] [datetime] NOT NULL,
[FavoriteCount] [int] NULL,
[LastActivityDate] [datetime] NOT NULL,
[LastEditDate] [datetime] NULL,
[LastEditorDisplayName] [nvarchar](40) NULL,
[LastEditorUserId] [int] NULL,
[OwnerUserId] [int] NULL,
[ParentId] [int] NULL,
[PostTypeId] [int] NOT NULL,
[Score] [int] NOT NULL,
[Tags] [nvarchar](150) NULL,
[Title] [nvarchar](250) NULL,
[ViewCount] [int] NOT NULL
)
CREATE NONCLUSTERED INDEX ndx_PostsDestination_Id ON PostsDestination(Id)
Ich versuche dann, die Pfostentabelle in diese Tabelle zu kopieren ...
INSERT INTO PostsDestination WITH(TABLOCK)
SELECT * FROM Posts ORDER BY Id
Wenn ich mir fn_dblog und die Verwendung der Protokolldatei ansehe, sehe ich, dass ich dadurch keine minimale Protokollierung erhalte. Ich habe gelesen, dass für Versionen vor 2016 das Ablaufverfolgungsflag 610 erforderlich ist, um sich minimal in indizierten Tabellen zu protokollieren. Ich habe auch versucht, dies festzulegen, aber immer noch keine Freude.
Vermutlich fehlt mir hier etwas?
EDIT - Mehr Info
Um weitere Informationen hinzuzufügen, benutze ich das folgende Verfahren, das ich geschrieben habe, um zu versuchen, minimale Protokollierung zu erkennen. Vielleicht stimmt hier etwas nicht ...
/*
Example Usage...
EXEC sp_GetLogUseStats
@Sql = '
INSERT INTO PostsDestination
SELECT TOP 500000 * FROM Posts ORDER BY Id ',
@Schema = 'dbo',
@Table = 'PostsDestination',
@ClearData = 1
*/
CREATE PROCEDURE [dbo].[sp_GetLogUseStats]
(
@Sql NVARCHAR(400),
@Schema NVARCHAR(20),
@Table NVARCHAR(200),
@ClearData BIT = 0
)
AS
IF @ClearData = 1
BEGIN
TRUNCATE TABLE PostsDestination
END
/*Checkpoint to clear log (Assuming Simple/Bulk Recovery Model*/
CHECKPOINT
/*Snapshot of logsize before query*/
CREATE TABLE #BeforeLogUsed(
[Db] NVARCHAR(100),
LogSize NVARCHAR(30),
Used NVARCHAR(50),
Status INT
)
INSERT INTO #BeforeLogUsed
EXEC('DBCC SQLPERF(logspace)')
/*Run Query*/
EXECUTE sp_executesql @SQL
/*Snapshot of logsize after query*/
CREATE TABLE #AfterLLogUsed(
[Db] NVARCHAR(100),
LogSize NVARCHAR(30),
Used NVARCHAR(50),
Status INT
)
INSERT INTO #AfterLLogUsed
EXEC('DBCC SQLPERF(logspace)')
/*Return before and after log size*/
SELECT
CAST(#AfterLLogUsed.Used AS DECIMAL(12,4)) - CAST(#BeforeLogUsed.Used AS DECIMAL(12,4)) AS LogSpaceUsersByInsert
FROM
#BeforeLogUsed
LEFT JOIN #AfterLLogUsed ON #AfterLLogUsed.Db = #BeforeLogUsed.Db
WHERE
#BeforeLogUsed.Db = DB_NAME()
/*Get list of affected indexes from insert query*/
SELECT
@Schema + '.' + so.name + '.' + si.name AS IndexName
INTO
#IndexNames
FROM
sys.indexes si
JOIN sys.objects so ON si.[object_id] = so.[object_id]
WHERE
si.name IS NOT NULL
AND so.name = @Table
/*Insert Record For Heap*/
INSERT INTO #IndexNames VALUES(@Schema + '.' + @Table)
/*Get log recrod sizes for heap and/or any indexes*/
SELECT
AllocUnitName,
[operation],
AVG([log record length]) AvgLogLength,
SUM([log record length]) TotalLogLength,
COUNT(*) Count
INTO #LogBreakdown
FROM
fn_dblog(null, null) fn
INNER JOIN #IndexNames ON #IndexNames.IndexName = allocunitname
GROUP BY
[Operation], AllocUnitName
ORDER BY AllocUnitName, operation
SELECT * FROM #LogBreakdown
SELECT AllocUnitName, SUM(TotalLogLength) TotalLogRecordLength
FROM #LogBreakdown
GROUP BY AllocUnitName
Einfügen in einen Heap ohne Indizes und TABLOCK mit folgendem Code ...
EXEC sp_GetLogUseStats
@Sql = '
INSERT INTO PostsDestination
SELECT * FROM Posts ORDER BY Id ',
@Schema = 'dbo',
@Table = 'PostsDestination',
@ClearData = 1
Ich bekomme diese Ergebnisse
Bei 0,0024 MB Protokolldatei-Wachstum, sehr kleinen Protokolldatensatzgrößen und sehr wenigen davon bin ich froh, dass hier nur minimale Protokollierung verwendet wird.
Wenn ich dann einen nicht geclusterten Index für id ...
CREATE INDEX ndx_PostsDestination_Id ON PostsDestination(Id)
Dann starte nochmal meinen selben Einsatz ...
Ich erhalte nicht nur keine minimale Protokollierung für den nicht gruppierten Index, sondern habe ihn auch auf dem Heap verloren. Nachdem ich einige weitere Tests durchgeführt habe, scheint es, dass wenn ich ID geclustert habe, es minimal protokolliert, aber von dem, was ich 2016+ gelesen habe, sollte es minimal auf einem Heap mit nicht geclustertem Index protokolliert werden, wenn Tablock verwendet wird.
FINAL EDIT :
Ich habe Microsoft das Verhalten in SQL Server UserVoice gemeldet und werde aktualisiert, wenn ich eine Antwort erhalte. Ich habe auch die vollständigen Details der minimalen Protokollszenarien aufgeschrieben, mit denen ich unter https://gavindraper.com/2018/05/29/SQL-Server-Minimal-Logging-Inserts/ nicht arbeiten konnte.