Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Logging Minimal dengan INSERT…PILIH ke Tabel Cluster Kosong

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 Anda menggunakan metode INSERT … SELECT, petunjuk ORDER tidak harus ditentukan, tetapi baris harus dalam urutan yang sama dengan indeks berkerumun.

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).
  • 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 dengan TABLOCK dan DMRequestSort setel ke benar menggunakan RowsetBulk 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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Menghubungkan ke Informix (IDS12 DB) di IRI Workbench

  2. Opsi Tingkat Database Azure SQL yang Diperbarui

  3. Bagaimana Desain Basis Data Membantu Mengatur Guru, Pelajaran, dan Siswa?

  4. Kebiasaan buruk :Berfokus hanya pada ruang disk saat memilih kunci

  5. Bagaimana Rencana Paralel Memulai – Bagian 3