Mengapa INSERT
pernyataan kedua ~ 5x lebih lambat dari yang pertama?
Dari jumlah data log yang dihasilkan, saya pikir yang kedua tidak memenuhi syarat untuk minimal logging. Namun, dokumentasi dalam Panduan Kinerja Pemuatan Data menunjukkan bahwa kedua sisipan harus dapat dicatat secara minimal. Jadi jika logging minimum adalah perbedaan kinerja utama, mengapa permintaan kedua tidak memenuhi syarat untuk logging minimal? Apa yang bisa dilakukan untuk memperbaiki situasi?
Kueri # 1: Memasukkan baris 5MM menggunakan INSERT ... WITH (TABLOCK)
Pertimbangkan kueri berikut, yang menyisipkan baris 5MM ke tumpukan. Kueri ini dieksekusi di 1 second
dan menghasilkan 64MB
data log transaksi seperti yang dilaporkan oleh sys.dm_tran_database_transactions
.
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
FROM dbo.fiveMillionNumbers
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Kueri # 2: Memasukkan data yang sama, tetapi SQL meremehkan # baris
Sekarang pertimbangkan permintaan yang sangat mirip ini, yang beroperasi pada data yang persis sama tetapi kebetulan mengambil dari tabel (atau SELECT
pernyataan kompleks dengan banyak bergabung dalam kasus produksi aktual saya) di mana perkiraan kardinalitas terlalu rendah. Kueri ini dijalankan di 5.5 seconds
dan menghasilkan 461MB
data log transaksi.
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that produces 5MM rows but SQL estimates just 1000 rows
FROM dbo.fiveMillionNumbersBadEstimate
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Skrip lengkap
Lihat Pastebin ini untuk set lengkap skrip untuk menghasilkan data pengujian dan menjalankan salah satu skenario ini. Perhatikan bahwa Anda harus menggunakan database yang ada dalam SIMPLE
model pemulihan .
Konteks bisnis
Kami setengah sering berpindah-pindah di sekitar jutaan baris data, dan penting agar operasi ini seefisien mungkin, baik dalam hal waktu eksekusi maupun beban I / O disk. Kami awalnya mendapat kesan bahwa membuat heap table dan menggunakan INSERT...WITH (TABLOCK)
adalah cara yang baik untuk melakukan ini, tetapi sekarang menjadi kurang percaya diri mengingat kami mengamati situasi yang ditunjukkan di atas dalam skenario produksi aktual (walaupun dengan pertanyaan yang lebih kompleks, bukan versi yang disederhanakan di sini).
sumber
SELECT
pernyataan kompleks dengan banyak gabungan yang menghasilkan hasil yang ditetapkan untukINSERT
. Gabungan ini menghasilkan perkiraan kardinalitas yang buruk untuk operator penyisipan tabel akhir (yang telah saya simulasikan dalam skrip repro melaluiUPDATE STATISTICS
panggilan buruk ), dan karena itu tidak sesederhana mengeluarkanUPDATE STATISTICS
perintah untuk memperbaiki masalah. Saya sepenuhnya setuju bahwa menyederhanakan kueri sehingga lebih mudah bagi Penaksir Kardinalitas untuk memahami mungkin merupakan pendekatan yang baik, tetapi itu bukan trival untuk menerapkan logika bisnis yang rumit.Memperluas gagasan Paul, solusi jika Anda benar-benar putus asa adalah dengan menambahkan tabel dummy yang menjamin bahwa perkiraan jumlah baris untuk insert akan cukup tinggi untuk kualitas untuk optimalisasi pemuatan massal. Saya mengonfirmasi bahwa ini mendapat pencatatan minimum dan meningkatkan kinerja kueri.
Takeaways terakhir
SELECT...INTO
untuk operasi penyisipan satu kali jika diperlukan penebangan minimal. Seperti yang ditunjukkan Paul, ini akan memastikan penebangan minimal terlepas dari estimasi barisArtikel terkait
Posting blog Paul White Mei 2019 Minimal Masuk dengan INSERT… PILIH ke Heap Tables mencakup beberapa informasi ini secara lebih rinci.
sumber