Saya sedang menguji sisipan logging minimal dalam skenario yang berbeda dan dari apa yang saya baca INSERT INTO SELECT menjadi tumpukan dengan indeks non clustered menggunakan TABLOCK dan SQL Server 2016+ harus minimal login, namun dalam kasus saya ketika melakukan ini saya mendapatkan penebangan penuh. Basis data saya berada dalam model pemulihan sederhana dan saya berhasil mendapatkan sisipan yang minimal dicatat di heap tanpa indeks dan TABLOCK.
Saya menggunakan cadangan lama dari database Stack Overflow untuk menguji dan telah membuat replikasi tabel Posts dengan skema berikut ...
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)
Saya kemudian mencoba menyalin tabel posting ke tabel ini ...
INSERT INTO PostsDestination WITH(TABLOCK)
SELECT * FROM Posts ORDER BY Id
Dari melihat fn_dblog dan penggunaan file log saya bisa melihat saya tidak mendapatkan minimal logging dari ini. Saya telah membaca bahwa versi sebelum 2016 memerlukan jejak bendera 610 untuk masuk secara minimum ke tabel yang diindeks, saya juga telah mencoba mengatur ini tetapi masih tidak ada sukacita.
Saya kira saya kehilangan sesuatu di sini?
EDIT - Info Lebih Lanjut
Untuk menambahkan lebih banyak info, saya menggunakan prosedur berikut yang saya tulis untuk mencoba mendeteksi penebangan minimal, mungkin ada yang salah di sini ...
/*
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
Memasukkan ke heap tanpa indeks dan TABLOCK menggunakan kode berikut ...
EXEC sp_GetLogUseStats
@Sql = '
INSERT INTO PostsDestination
SELECT * FROM Posts ORDER BY Id ',
@Schema = 'dbo',
@Table = 'PostsDestination',
@ClearData = 1
Saya mendapatkan hasil ini
Pada pertumbuhan file log 0,0024mb, ukuran catatan log sangat kecil dan sangat sedikit dari mereka saya senang bahwa ini menggunakan logging minimal.
Jika saya kemudian membuat indeks non clustered pada id ...
CREATE INDEX ndx_PostsDestination_Id ON PostsDestination(Id)
Kemudian jalankan kembali insert yang sama ...
Bukan saja saya tidak mendapatkan minimum logging pada indeks non clustered tapi saya juga kehilangan itu di heap. Setelah melakukan beberapa tes lagi tampaknya jika saya membuat ID clustered itu log minimal tetapi dari apa yang saya baca 2016+ harus minimal login ke heap dengan indeks non clustered ketika tablock digunakan.
EDIT AKHIR :
Saya telah melaporkan perilaku ke Microsoft pada SQL Server UserVoice dan akan memperbarui jika saya mendapat respons. Saya juga telah menulis detail lengkap dari skenario log minimal yang saya tidak bisa mulai bekerja di https://gavindraper.com/2018/05/29/SQL-Server-Minimal-Logging-Inserts/