Pengantar
Mencapai pencatatan log minimal menggunakan INSERT...SELECT
menjadi kosong target indeks berkerumun tidak sesederhana yang dijelaskan dalam Panduan Pemuatan Kinerja Data .
Pos ini memberikan detail baru tentang persyaratan untuk logging minimal ketika target penyisipan adalah indeks berkerumun tradisional yang kosong. (Kata “tradisional” tidak termasuk columnstore dan dioptimalkan memori ('Hekaton') tabel berkerumun). Untuk ketentuan yang berlaku ketika tabel target adalah heap, lihat artikel sebelumnya di seri ini.
Ringkasan untuk Tabel Tergugus
Panduan Performa Pemuatan Data berisi ringkasan tingkat tinggi tentang kondisi yang diperlukan untuk logging minimal ke dalam tabel berkerumun:
Pos ini berkaitan dengan hanya baris atas . Ini menyatakan bahwa TABLOCK
dan ORDER
petunjuk diperlukan, dengan catatan yang berbunyi:
Jika menggunakan BULK INSERT, petunjuk urutan harus digunakan.
Target Kosong dengan Kunci Tabel
Ringkasan baris atas menunjukkan bahwa semua menyisipkan ke indeks berkerumun kosong akan dilog minimal selama TABLOCK
dan ORDER
petunjuk ditentukan. TABLOCK
petunjuk diperlukan untuk mengaktifkan RowSetBulk
fasilitas seperti yang digunakan untuk beban massal meja tumpukan. Sebuah ORDER
petunjuk diperlukan untuk memastikan baris tiba di Sisipkan Indeks Berkelompok rencanakan operator dalam indeks target urutan kunci . Tanpa jaminan ini, SQL Server mungkin menambahkan baris indeks yang tidak diurutkan dengan benar, yang tidak akan baik.
Tidak seperti metode pemuatan massal lainnya, ini tidak mungkin untuk menentukan ORDER
yang diperlukan petunjuk tentang INSERT...SELECT
penyataan. Petunjuk ini tidak sama seperti menggunakan ORDER BY
klausa pada INSERT...SELECT
penyataan. Sebuah ORDER BY
klausa pada INSERT
hanya menjamin cara identitas nilai ditetapkan, bukan urutan penyisipan baris.
Untuk INSERT...SELECT
, SQL Server membuat penentuannya sendiri apakah akan memastikan baris disajikan ke Sisipkan Indeks Berkelompok operator dalam urutan kunci atau tidak. Hasil penilaian ini terlihat dalam rencana eksekusi melalui DMLRequestSort
milik Sisipkan operator. DMLRequestSort
properti harus disetel ke benar untuk INSERT...SELECT
ke dalam indeks untuk dilog minimal . Jika disetel ke false , pencatatan log minimal tidak dapat terjadi.
Memiliki DMLRequestSort
setel ke benar adalah satu-satunya jaminan yang dapat diterima memasukkan pemesanan input untuk SQL Server. Seseorang mungkin memeriksa rencana eksekusi dan memprediksi bahwa baris harus/akan/harus tiba dalam urutan indeks berkerumun, tetapi tanpa jaminan internal tertentu disediakan oleh DMLRequestSort
, penilaian itu tidak berarti apa-apa.
Ketika DMLRequestSort
apakah benar , SQL Server mungkin memperkenalkan Urutkan . yang eksplisit operator dalam rencana eksekusi. Jika secara internal dapat menjamin pemesanan dengan cara lain, Urut dapat dihilangkan. Jika alternatif pengurutan dan tanpa pengurutan tersedia, pengoptimal akan membuat berbasis biaya pilihan. Analisis biaya tidak memperhitungkan logging minimal secara langsung; ini didorong oleh manfaat yang diharapkan dari I/O sekuensial dan penghindaran pemisahan halaman.
DMLRequestSort Conditions
Kedua tes berikut harus lulus agar SQL Server memilih untuk menyetel DMLRequestSort
untuk benar saat memasukkan ke indeks berkerumun kosong dengan penguncian tabel yang ditentukan:
- Perkiraan lebih dari 250 baris di sisi input Sisipkan Indeks Berkelompok operator; dan
- Sebuah perkiraan ukuran data lebih dari 2 halaman . Perkiraan ukuran data bukan bilangan bulat, sehingga hasil 2.001 halaman akan memenuhi ketentuan ini.
(Ini mungkin mengingatkan Anda tentang kondisi heap logging minimal , tetapi perkiraan required yang dibutuhkan ukuran data di sini adalah dua halaman, bukan delapan.)
Perhitungan Ukuran Data
perkiraan ukuran data perhitungan di sini tunduk pada kebiasaan yang sama yang dijelaskan dalam artikel sebelumnya untuk heap, kecuali bahwa RID 8-byte tidak hadir.
Untuk SQL Server 2012 dan sebelumnya, ini berarti 5 byte tambahan per baris disertakan dalam penghitungan ukuran data:Satu byte untuk bit internal internal bendera, dan empat byte untuk uniquifier (digunakan dalam perhitungan bahkan untuk indeks unik, yang tidak menyimpan uniquifier ).
Untuk SQL Server 2014 dan yang lebih baru, uniquifier dihilangkan dengan benar untuk unik indeks, tetapi satu byte tambahan untuk bit internal internal bendera dipertahankan.
Demo
Skrip berikut harus dijalankan pada instance SQL Server pengembangan di basis data pengujian baru setel untuk menggunakan SIMPLE
atau BULK_LOGGED
model pemulihan.
Demo memuat 268 baris ke dalam tabel cluster baru menggunakan INSERT...SELECT
dengan TABLOCK
, dan laporan tentang catatan log transaksi yang dihasilkan.
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 -- Clear the log CHECKPOINT; GO -- Insert rows INSERT dbo.Test WITH (TABLOCK) (c1) SELECT TOP (268) 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)';
(Jika Anda menjalankan skrip di SQL Server 2012 atau sebelumnya, ubah TOP
klausa dalam naskah dari 268 hingga 252, untuk alasan yang akan dijelaskan sebentar lagi.)
Keluaran menunjukkan bahwa semua baris yang dimasukkan sepenuhnya dicatat meskipun kosong target tabel berkerumun dan TABLOCK
petunjuk:
Ukuran data sisipan yang dihitung
Properti rencana eksekusi dari Sisipkan Indeks Berkelompok operator menunjukkan bahwa DMLRequestSort
disetel ke salah . Ini karena meskipun perkiraan jumlah baris yang akan disisipkan lebih dari 250 (memenuhi persyaratan pertama), dihitung ukuran data tidak melebihi dua halaman 8KB.
Rincian perhitungan (untuk SQL Server 2014 dan seterusnya) adalah sebagai berikut:
- Total panjang tetap ukuran kolom =54 byte :
- Ketik id 104
bit
=1 byte (internal). - Ketik id 56
integer
=4 byte (id
kolom). - Ketik id 56
integer
=4 byte (c1
kolom). - Ketik id 175
char(45)
=45 byte (padding
kolom).
- Ketik id 104
- Bitmap nol =3 byte .
- Tajuk baris overhead =4 byte .
- Ukuran baris yang dihitung =54 + 3 + 4 =61 byte .
- Ukuran data yang dihitung =61 byte * 268 baris =16.348 byte .
- Halaman data terhitung =16.384 / 8192 =1.99560546875 .
Ukuran baris terhitung (61 byte) berbeda dari ukuran penyimpanan baris sebenarnya (60 byte) dengan tambahan satu byte metadata internal yang ada dalam aliran penyisipan. Perhitungan juga tidak memperhitungkan 96 byte yang digunakan pada setiap halaman oleh header halaman, atau hal-hal lain seperti overhead versi baris. Perhitungan yang sama pada SQL Server 2012 menambahkan 4 byte lebih lanjut per baris untuk uniquifier (yang tidak ada dalam indeks unik seperti yang disebutkan sebelumnya). Byte ekstra berarti lebih sedikit baris yang diharapkan untuk muat di setiap halaman:
- Ukuran baris yang dihitung =61 + 4 =65 byte .
- Ukuran data yang dihitung =65 byte * 252 baris =16.380 byte
- Halaman data terhitung =16.380 / 8192 =1.99951171875 .
Mengubah TOP
klausa dari 268 baris menjadi 269 (atau dari 252 hingga 253 untuk 2012) membuat perhitungan ukuran data yang diharapkan hanya tip di atas ambang batas minimum 2 halaman:
- SQL Server 2014
- 61 byte * 269 baris =16.409 byte.
- 16.409 / 8192 =2.0030517578125 halaman.
- SQL Server 2012
- 65 byte * 253 baris =16.445 byte.
- 16.445 / 8192 =2.0074462890625 halaman.
Dengan kondisi kedua sekarang juga terpenuhi, DMLRequestSort
disetel ke benar , dan pencatatan log minimal tercapai, seperti yang ditunjukkan pada output di bawah ini:
Beberapa tempat menarik lainnya:
- Total 79 catatan log dihasilkan, dibandingkan dengan 328 untuk versi yang sepenuhnya dicatat. Lebih sedikit catatan log adalah hasil yang diharapkan dari logging minimal.
LOP_BEGIN_XACT
catatan di dicatat secara minimal record menyimpan ruang log yang relatif besar (masing-masing 9436 byte).- Salah satu nama transaksi yang tercantum dalam catatan log adalah “offline index build” . Meskipun kami tidak meminta indeks dibuat seperti itu, memuat baris secara massal ke dalam indeks kosong pada dasarnya adalah operasi yang sama.
- Yang tercatat sepenuhnya insert mengambil kunci eksklusif tingkat tabel (
Tab-X
), sedangkan dicatat secara minimal insert mengambil modifikasi skema (Sch-M
) seperti halnya pembuatan indeks offline 'nyata'. - Memuat massal tabel berkerumun kosong menggunakan
INSERT...SELECT
denganTABLOCK
danDMRequestSort
setel ke benar menggunakanRowsetBulk
mekanisme, seperti dicatat secara minimal beban tumpukan dilakukan di artikel sebelumnya.
Estimasi Kardinalitas
Hati-hati dengan perkiraan kardinalitas rendah di Sisipkan Indeks Berkelompok operator. Jika salah satu dari ambang batas diperlukan untuk menyetel DMLRequestSort
untuk benar tidak tercapai karena estimasi kardinalitas yang tidak akurat, sisipan akan dilog sepenuhnya , terlepas dari jumlah baris aktual dan ukuran data total yang ditemui pada waktu eksekusi.
Misalnya, mengubah TOP
klausa dalam skrip demo untuk menggunakan variabel menghasilkan kardinalitas tetap tebak dari 100 baris, yang berada di bawah minimum 251 baris:
-- Insert rows DECLARE @NumRows bigint = 269; INSERT dbo.Test WITH (TABLOCK) (c1) SELECT TOP (@NumRows) CHECKSUM(NEWID()) FROM master.dbo.spt_values AS SV;
Rencanakan Caching
DMLRequestSort
properti disimpan sebagai bagian dari paket yang di-cache. Saat paket yang di-cache digunakan kembali , nilai DMLRequestSort
tidak dihitung ulang pada waktu eksekusi, kecuali kompilasi ulang terjadi. Perhatikan bahwa kompilasi ulang tidak terjadi untuk TRIVIAL
rencana berdasarkan perubahan statistik atau kardinalitas tabel.
Salah satu cara untuk menghindari perilaku tak terduga karena caching adalah dengan menggunakan OPTION (RECOMPILE)
petunjuk. Ini akan memastikan pengaturan yang sesuai untuk DMLRequestSort
dihitung ulang, dengan biaya kompilasi pada setiap eksekusi.
Bendera Jejak
Dimungkinkan untuk memaksa DMLRequestSort
untuk disetel ke benar dengan menyetel tidak berdokumen dan tidak didukung trace flag 2332, seperti yang saya tulis di Mengoptimalkan kueri T-SQL yang mengubah data. Sayangnya, ini tidak memengaruhi pencatatan log minimal kelayakan untuk tabel berkerumun kosong — sisipan masih harus diperkirakan lebih dari 250 baris dan 2 halaman. Tanda jejak ini memengaruhi pembuatan log minimal . lainnya skenario, yang dibahas di bagian akhir seri ini.
Ringkasan
Memuat massal kosong indeks berkerumun menggunakan INSERT...SELECT
menggunakan kembali RowsetBulk
mekanisme yang digunakan untuk memuat tabel tumpukan secara massal. Ini membutuhkan penguncian tabel (biasanya dicapai dengan TABLOCK
petunjuk) dan ORDER
petunjuk. Tidak ada cara untuk menambahkan ORDER
petunjuk ke INSERT...SELECT
penyataan. Akibatnya, mencapai logging minimal ke dalam tabel berkerumun kosong mengharuskan DMLRequestSort
properti dari Sisipkan Indeks Berkelompok operator disetel ke benar . Ini menjamin ke SQL Server baris yang disajikan ke Sisipkan operator akan tiba dalam urutan kunci indeks target. Efeknya sama seperti saat menggunakan ORDER
petunjuk tersedia untuk metode penyisipan massal lainnya seperti BULK INSERT
dan bcp
.
Agar DMLRequestSort
untuk disetel ke benar , harus ada:
- Lebih dari 250 baris perkiraan untuk dimasukkan; dan
- Sebuah perkiraan masukkan ukuran data lebih dari dua halaman .
Perkiraan masukkan perhitungan ukuran data tidak cocok dengan hasil perkalian rencana eksekusi perkiraan jumlah baris dan perkiraan ukuran baris properti di input ke Sisipkan operator. Perhitungan internal (salah) mencakup satu atau lebih kolom internal di aliran sisipan, yang tidak bertahan dalam indeks akhir. Perhitungan internal juga tidak memperhitungkan header halaman atau overhead lain seperti versi baris.
Saat menguji atau men-debug logging minimal masalah, waspadalah terhadap perkiraan kardinalitas rendah, dan ingat bahwa pengaturan DMLRequestSort
di-cache sebagai bagian dari rencana eksekusi.
Bagian akhir dari seri ini merinci kondisi yang diperlukan untuk mencapai logging minimal tanpa menggunakan RowsetBulk
mekanisme. Ini berhubungan langsung dengan fasilitas baru yang ditambahkan di bawah bendera pelacakan 610 ke SQL Server 2008, kemudian diubah menjadi aktif secara default dari SQL Server 2016 dan seterusnya.