Kondisi Pencatatan Minimal dalam SQL

9

Saya telah menulis skrip untuk menguji klaim yang dibuat pada halaman ini http://technet.microsoft.com/en-us/library/dd425070(v=sql.100).aspx dalam tabel berjudul Meringkas Kondisi Pencatatan Minimal tentang kapan penebangan minimal tidak atau tidak terjadi.

Menggunakan skrip ini, saya menemukan bahwa jumlah Panjang Catatan Log untuk masing-masing jenis sisipan berbeda adalah sebagai berikut:

  • Tumpukan kosong tanpa tablock 60000
  • Tumpukan kosong dengan tablock 56000
  • Tumpukan tidak kosong tanpa tablock 60000
  • Tumpukan non kosong dengan tablock 56000
  • Heap plus index kosongkan tanpa tablock 126188
  • Heap plus index kosong dengan tablock 114188
  • Heap plus indeks tidak kosong tanpa tablock 138696
  • Heap plus index tidak kosong dengan tablock 112000
  • Cluster kosong memerintahkan tidak ada tablock 64168
  • Cluster kosong dipesan dengan tablock 56168
  • Cluster kosong tidak terurut, tidak ada tablock 73388
  • Cluster kosong tidak berurutan dengan tablock 65388
  • Cluster tidak kosong tanpa tablock 63912
  • Cluster tidak kosong dengan tablock 55944
  • Klaster plus indeks kosong tanpa tablock 124336
  • Cluster plus index empty dengan tablock 108336
  • Cluster plus indeks non kosong tanpa tablock 123876
  • Cluster plus index tidak kosong dengan tablock 107924

Beberapa dari angka-angka ini tampaknya tidak cocok dengan tabel pada halaman technet. Khususnya:

  • Tampaknya tidak ada perbedaan dalam pencatatan antara memasukkan ke dalam tabel kosong vs tidak kosong, tetapi halaman tersebut mengklaim bahwa harus ada pencatatan penuh saat memasukkan ke dalam cluster yang tidak kosong tanpa tablock
  • Memasukkan dengan tablock ke tumpukan atau cluster dengan dan indeks tampaknya mengurangi logging, tetapi halaman mengklaim harus ada logging penuh.
  • Saat menggunakan metode penyisipan SELECT INTO, tidak ada baris di fn_dblog yang operasinya disisipkan, tetapi halaman mencantumkan metode ini sebagai operasi pemuatan massal yang harusnya memiliki perilaku yang dijelaskan dalam tabel

Sebagai referensi, ini dijalankan pada database SQL express, dan ketika saya menjalankan DBCC TRACESTATUS (610) semuanya adalah 0.

Adakah yang bisa membantu menjelaskan mengapa saya mungkin melihat perbedaan ini?

Untuk referensi kode di bawah ini:

SET NOCOUNT ON

CREATE TABLE numbers (num INT)
CREATE TABLE numbersUnordered (num INT)

Declare @cnt int
Select @cnt=0
while (@cnt<500)
BEGIN
INSERT INTO NUMBERS(num) SELECT @cnt
SELECT @cnt=@cnt+1
END

Select @cnt=0
while (@cnt<250)
BEGIN
INSERT INTO numbersUnordered(num) SELECT @cnt*2
SELECT @cnt=@cnt+1
END

Select @cnt=0
while (@cnt<250)
BEGIN
INSERT INTO numbersUnordered(num) SELECT @cnt*2+1
SELECT @cnt=@cnt+1
END


---- heap empty without tablock
CREATE TABLE noKey1 (val INT)

INSERT INTO noKey1 (val)
SELECT * FROM numbers

DECLARE @heapEmptyNoTablock INT

SELECT @heapEmptyNoTablock = SUM([Log Record Length])
FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%noKey1%'
AND operation like '%insert%'

---- heap empty with tablock
CREATE TABLE noKey2 (val INT)

INSERT INTO noKey2 WITH(TABLOCK) (val)
SELECT * FROM numbers

DECLARE @heapEmptyTablock INT

SELECT @heapEmptyTablock = SUM([Log Record Length])
FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%noKey2%'
AND operation like '%insert%'

---- heap non empty without tablock
CREATE TABLE noKey3 (val INT)

INSERT INTO noKey3 WITH(TABLOCK) (val)
SELECT * FROM numbers

INSERT INTO noKey3 (val)
SELECT num+5 FROM numbers

DECLARE @heapNonEmptyNoTablock INT

SELECT @heapNonEmptyNoTablock = SUM(a.[Log Record Length])
FROM 
(SELECT TOP 500 * FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%noKey3%'
AND operation like '%insert%'
ORDER BY [Current LSN] desc) a

---- heap non empty with tablock
CREATE TABLE noKey4 (val INT)

INSERT INTO noKey4 WITH(TABLOCK) (val)
SELECT * FROM numbers

INSERT INTO noKey4 WITH (TABLOCK) (val)
SELECT num+5 FROM numbers

DECLARE @heapNonEmptyTablock INT

SELECT @heapNonEmptyTablock = SUM(a.[Log Record Length])
FROM 
(SELECT TOP 500 * FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%noKey4%'
AND operation like '%insert%'
ORDER BY [Current LSN] desc) a

--- heap plus index empty without tablock
CREATE TABLE noKey5 (val INT)
CREATE INDEX MSindex1
ON noKey5 (val)

INSERT INTO noKey5 (val)
SELECT * FROM numbers

DECLARE @heapIndexEmptyNoTablock INT

SELECT @heapIndexEmptyNoTablock = SUM([Log Record Length])
FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%noKey5%'
AND operation like '%insert%'


--- heap plus index empty with tablock
CREATE TABLE noKey6 (val INT)
CREATE INDEX MSindex2
ON noKey6 (val)

INSERT INTO noKey6 WITH(TABLOCK) (val)
SELECT * FROM numbers

DECLARE @heapIndexEmptyTablock INT

SELECT @heapIndexEmptyTablock = SUM([Log Record Length])
FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%noKey6%'
AND operation like '%insert%'

--- heap plus index non empty without tablock
CREATE TABLE noKey7 (val INT)
CREATE INDEX MSindex3
ON noKey7 (val)

INSERT INTO noKey7 WITH(TABLOCK) (val)
SELECT * FROM numbers

INSERT INTO noKey7 (val)
SELECT num+5 FROM numbers

DECLARE @heapIndexNonEmptyNoTablock INT

SELECT @heapIndexNonEmptyNoTablock = SUM(a.[Log Record Length])
FROM 
(SELECT TOP 1000 * FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%noKey7%'
AND operation like '%insert%'
ORDER BY [Current LSN] desc) a

--- heap plus index non empty with tablock
CREATE TABLE noKey8 (val INT)
CREATE INDEX MSindex4
ON noKey7 (val)

INSERT INTO noKey8 WITH(TABLOCK) (val)
SELECT * FROM numbers

INSERT INTO noKey8 WITH(TABLOCK) (val)
SELECT num+5 FROM numbers

DECLARE @heapIndexNonEmptyTablock INT

SELECT @heapIndexNonEmptyTablock = SUM(a.[Log Record Length])
FROM 
(SELECT TOP 1000 * FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%noKey8%'
AND operation like '%insert%'
ORDER BY [Current LSN] desc) a

--- cluster empty ordered without tablock
CREATE TABLE withKey1 (val INT PRIMARY KEY)

INSERT INTO withKey1 (val)
SELECT * FROM numbers

DECLARE @clusterEmptyNoTablock INT

SELECT @clusterEmptyNoTablock = SUM([Log Record Length])
FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%withKey1%'
AND operation like '%insert%'

--- cluster empty ordered with tablock
CREATE TABLE withKey2 (val INT PRIMARY KEY)

INSERT INTO withKey2 WITH(TABLOCK) (val)
SELECT * FROM numbers

DECLARE @clusterEmptyTablock INT

SELECT @clusterEmptyTablock = SUM([Log Record Length])
FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%withKey2%'
AND operation like '%insert%'

--- cluster empty unordered without tablock
CREATE TABLE withKey5 (val INT PRIMARY KEY)

INSERT INTO withKey5 (val)
SELECT * FROM numbersUnordered

DECLARE @clusterEmptyNoTablockUnordered INT

SELECT @clusterEmptyNoTablockUnordered = SUM([Log Record Length])
FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%withKey5%'
AND operation like '%insert%'

--- cluster empty undordered with tablock
CREATE TABLE withKey6 (val INT PRIMARY KEY)

INSERT INTO withKey6 WITH(TABLOCK) (val)
SELECT * FROM numbersUnordered

DECLARE @clusterEmptyTablockUnordered INT

SELECT @clusterEmptyTablockUnordered = SUM([Log Record Length])
FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%withKey6%'
AND operation like '%insert%'

--- cluster non empty no tablock
CREATE TABLE withKey7 (val INT PRIMARY KEY)

INSERT INTO withKey7 (val)
SELECT num FROM numbers

INSERT INTO withKey7 (val)
SELECT num+500 FROM numbers

DECLARE @clusterNonEmptyNoTablock INT

SELECT @clusterNonEmptyNoTablock = SUM(a.[Log Record Length])
FROM 
(SELECT TOP 500 * FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%withKey7%'
AND operation like '%insert%'
ORDER BY [Current LSN] desc) a

--- cluster non empty with tablock
CREATE TABLE withKey8 (val INT PRIMARY KEY)

INSERT INTO withKey8 WITH(TABLOCK) (val)
SELECT num FROM numbers

INSERT INTO withKey8 WITH(TABLOCK) (val)
SELECT num+500 FROM numbers

DECLARE @clusterNonEmptyTablock INT

SELECT @clusterNonEmptyTablock = SUM(a.[Log Record Length])
FROM 
(SELECT TOP 500 * FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%withKey8%'
AND operation like '%insert%'
ORDER BY [Current LSN] desc) a

--- cluster plus index empty no tablock
CREATE TABLE withKey9 (val INT PRIMARY KEY)
CREATE INDEX MSindex5
ON withKey9 (val)

INSERT INTO withKey9 (val)
SELECT * FROM numbers

DECLARE @clusterIndexEmptyNoTablock INT

SELECT @clusterIndexEmptyNoTablock = SUM([Log Record Length])
FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%withKey9%'
AND operation like '%insert%'

--- cluster plus index empty with tablock
CREATE TABLE withKey10 (val INT PRIMARY KEY)
CREATE INDEX MSindex6
ON withKey10 (val)

INSERT INTO withKey10 WITH(TABLOCK) (val)
SELECT * FROM numbers

DECLARE @clusterIndexEmptyTablock INT

SELECT @clusterIndexEmptyTablock = SUM([Log Record Length])
FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%withKey10%'
AND operation like '%insert%'

--- cluster plus index nonempty no tablock
CREATE TABLE withKey11 (val INT PRIMARY KEY)
CREATE INDEX MSindex7
ON withKey11 (val)

INSERT INTO withKey11 (val)
SELECT num FROM numbers

INSERT INTO withKey11 (val)
SELECT num+500 FROM numbers

DECLARE @clusterIndexNonEmptyNoTablock INT

SELECT @clusterIndexNonEmptyNoTablock = SUM(a.[Log Record Length])
FROM 
(SELECT TOP 1000 * FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%withKey11%'
AND operation like '%insert%'
ORDER BY [Current LSN] desc) a

--- cluster plus index nonempty with tablock
CREATE TABLE withKey12 (val INT PRIMARY KEY)
CREATE INDEX MSindex8
ON withKey12 (val)

INSERT INTO withKey12 WITH(TABLOCK) (val)
SELECT num FROM numbers

INSERT INTO withKey12 WITH(TABLOCK) (val)
SELECT num+500 FROM numbers

DECLARE @clusterIndexNonEmptyTablock INT

SELECT @clusterIndexNonEmptyTablock = SUM(a.[Log Record Length])
FROM 
(SELECT TOP 1000 * FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%withKey12%'
AND operation like '%insert%'
ORDER BY [Current LSN] desc) a

--- select into
/*SELECT * 
INTO selectIntoTable
FROM numbers

SELECT * FROM sys.fn_dblog(null, null)
WHERE allocunitname like '%selectIntoTable%'
AND operation like '%insert%'

DROP TABLE selectIntoTable
*/


PRINT 'Heap empty no tablock ' + CAST(@heapEmptyNoTablock AS VARCHAR)
PRINT 'Heap empty with tablock ' + CAST(@heapEmptyTablock AS VARCHAR)
PRINT 'Heap non empty no tablock ' + CAST(@heapNonEmptyNoTablock AS VARCHAR)
PRINT 'Heap non empty with tablock ' + CAST(@heapNonEmptyTablock AS VARCHAR)
PRINT 'Heap plus index empty no tablock ' + CAST(@heapIndexEmptyNoTablock AS VARCHAR)
PRINT 'Heap plus index empty with tablock ' + CAST(@heapIndexEmptyTablock AS VARCHAR)
PRINT 'Heap plus index non empty no tablock ' + CAST(@heapIndexNonEmptyNoTablock AS VARCHAR)
PRINT 'Heap plus index non empty with tablock ' + CAST(@heapIndexNonEmptyTablock AS VARCHAR)
PRINT 'Cluster empty ordered no tablock ' + CAST(@clusterEmptyNoTablock AS VARCHAR)
PRINT 'Cluster empty ordered with tablock ' + CAST(@clusterEmptyTablock AS VARCHAR)
PRINT 'Cluster empty unordered no tablock ' + CAST(@clusterEmptyNoTablockUnordered AS VARCHAR)
PRINT 'Cluster empty unordered with tablock ' + CAST(@clusterEmptyTablockUnordered AS VARCHAR)
PRINT 'Cluster non empty no tablock ' + CAST(@clusterNonEmptyNoTablock AS VARCHAR)
PRINT 'Cluster non empty with tablock ' + CAST(@clusterNonEmptyTablock AS VARCHAR)
PRINT 'Cluster plus index empty no tablock ' + CAST(@clusterIndexEmptyNoTablock AS VARCHAR)
PRINT 'Cluster plus index empty with tablock ' + CAST(@clusterIndexEmptyTablock AS VARCHAR)
PRINT 'Cluster plus index non empty no tablock ' + CAST(@clusterIndexNonEmptyNoTablock AS VARCHAR)
PRINT 'Cluster plus index non empty with tablock ' + CAST(@clusterIndexNonEmptyTablock AS VARCHAR)


DROP TABLE numbers
DROP TABLE numbersUnordered
DROP TABLE noKey1
DROP TABLE noKey2
DROP TABLE noKey3
DROP TABLE noKey4
DROP TABLE noKey5
DROP TABLE noKey6
DROP TABLE noKey7
DROP TABLE noKey8
DROP TABLE withKey1
DROP TABLE withKey2
DROP TABLE withKey5
DROP TABLE withKey6
DROP TABLE withKey7
DROP TABLE withKey8
DROP TABLE withKey9
DROP TABLE withKey10
DROP TABLE withKey11
DROP TABLE withKey12

sumber

Jawaban:

12

Beberapa dari angka-angka ini tampaknya tidak cocok dengan tabel pada halaman technet.

Ada perbedaan kecil dalam ukuran catatan log yang dihasilkan dalam tes Anda, tetapi ini disebabkan oleh perilaku pencatatan internal lainnya, bukan apakah penebangan minimal terjadi atau tidak.

Definisi yang baik tentang penebangan minimal disediakan oleh Sunil Agarwal dari tim Storage Engine:

Baris individual tidak dicatat dan hanya perubahan pada struktur alokasi halaman yang dicatat

Setiap tes di mana Anda melihat perubahan baris dicatat (misalnya LOP_INSERT_ROWS) tidak menggunakan logging minimal untuk unit alokasi terkait. Beberapa operasi dapat secara minimal dicatat sehubungan dengan satu unit alokasi (misalnya indeks) dan tidak minimal dicatat terhadap yang lain. Juga, dalam beberapa keadaan, menyisipkan ke halaman yang ada mungkin tidak dicatat secara minimal tetapi perubahan ke halaman yang baru dialokasikan mungkin.

Sebagian besar detail dapat ditemukan dalam serangkaian posting blog tim Mesin Penyimpanan:

Satu detail tidak dieksplorasi ada adalah bahwa untuk menjadi minimal-login (dalam SQL Server 2008 atau yang lebih baru) INSERT...SELECTperubahan ke struktur b-tree harus memiliki DMLRequestSortproperti operator rencana kueri disetel ke true. Ini berlaku untuk keadaan di mana Panduan Kinerja Pemuatan Data menunjukkan 'Tergantung': rencana kueri harus menggunakan pemeliharaan lebar (per-indeks) dengan DMLRequestSort=true.

Saya menulis lebih banyak tentang ini di Minimal Logging dengan INSERT… SELECT dan Fast Load Context .

Paul White 9
sumber