Pengantar
Sebuah Spool Indeks Bersemangat membaca semua baris dari operator turunannya ke meja kerja yang diindeks, sebelum mulai mengembalikan baris ke operator induknya. Dalam beberapa hal, spool indeks yang bersemangat adalah saran indeks yang hilang ultimate , tetapi tidak dilaporkan seperti itu.
Penilaian biaya
Memasukkan baris ke meja kerja yang diindeks relatif murah, tetapi tidak gratis. Pengoptimal harus mempertimbangkan bahwa pekerjaan yang terlibat menghemat lebih banyak daripada biayanya. Untuk itu agar menguntungkan spool, rencana harus diperkirakan mengkonsumsi baris dari spool lebih dari sekali. Jika tidak, itu mungkin juga melewatkan spool, dan hanya melakukan operasi yang mendasarinya satu kali.
- Agar dapat diakses lebih dari satu kali, spool harus muncul di sisi dalam dari nested loop join operator.
- Setiap iterasi dari loop harus mencari nilai kunci spool indeks tertentu yang disediakan oleh sisi luar loop.
Itu berarti bahwa bergabung harus mendaftar , bukan gabungan loop bersarang . Untuk perbedaan antara keduanya, silakan lihat artikel saya Terapkan versus Gabung Nested Loops.
Fitur penting
Sementara spool indeks yang bersemangat hanya dapat muncul di sisi dalam loop bersarang apply , ini bukan "spool kinerja". Kumparan indeks yang bersemangat tidak dapat dinonaktifkan dengan tanda jejak 8690 atau NO_PERFORMANCE_SPOOL
petunjuk kueri.
Baris yang dimasukkan ke dalam gulungan indeks biasanya tidak diurutkan sebelumnya ke dalam urutan kunci indeks, yang dapat mengakibatkan pemisahan halaman indeks. Bendera jejak tidak berdokumen 9260 dapat digunakan untuk menghasilkan Urutkan operator sebelum spool indeks untuk menghindari hal ini. Kelemahannya adalah biaya penyortiran tambahan dapat menghalangi pengoptimal untuk memilih opsi gulungan sama sekali.
SQL Server tidak mendukung penyisipan paralel ke indeks b-tree. Ini berarti bahwa segala sesuatu di bawah spool indeks bersemangat paralel berjalan pada satu utas. Operator di bawah spool masih (menyesatkan) ditandai dengan ikon paralelisme. Satu utas dipilih untuk menulis ke kumparan. Utas lainnya menunggu di EXECSYNC
sementara itu selesai. Setelah spool diisi, mungkin dibaca dari oleh utas paralel.
Spool indeks tidak memberi tahu pengoptimal bahwa mereka mendukung output yang dipesan oleh kunci indeks spool. Jika output yang diurutkan dari spool diperlukan, Anda mungkin melihat Sort . yang tidak perlu operator. Gulungan indeks yang bersemangat harus sering diganti dengan indeks permanen, jadi ini sering kali menjadi perhatian kecil.
Ada lima aturan pengoptimal yang dapat menghasilkan Eager Index Spool opsi (dikenal secara internal sebagai indeks saat itu juga ). Kami akan melihat tiga di antaranya secara mendetail untuk memahami dari mana kumpulan indeks yang bersemangat itu berasal.
SelToIndexOnTheFly
Ini adalah yang paling umum. Ini cocok dengan satu atau lebih pilihan relasional (alias filter atau predikat) tepat di atas operator akses data. SelToIndexOnTheFly
rule menggantikan predikat dengan predikat seek pada spool indeks yang bersemangat.
Demo
Sebuah Petualangan Karya contoh contoh database ditunjukkan di bawah ini:
SELECT P.ProductID, P.[Name], P.SafetyStockLevel, TH.Quantity FROM Production.Product AS P CROSS APPLY ( SELECT MAX(TH.Quantity) FROM Production.TransactionHistory AS TH WHERE TH.ProductID = P.ProductID AND TH.Quantity < P.SafetyStockLevel GROUP BY () ) AS TH (Quantity) WHERE P.[Name] LIKE N'A%';
Rencana eksekusi ini memiliki perkiraan biaya 3,0881 unit. Beberapa tempat menarik:
- Penggabungan Bagian Dalam Loop Bersarang operator adalah berlaku , dengan
ProductID
danSafetyStockLevel
dariProduct
tabel sebagai referensi luar . - Pada iterasi pertama penerapan, Eager Index Spool terisi penuh dari Clustered Index Scan dari
TransactionHistory
meja. - Meja kerja spool memiliki indeks berkerumun yang dikunci pada
(ProductID, Quantity)
. - Baris yang cocok dengan predikat
TH.ProductID = P.ProductID
danTH.Quantity < P.SafetyStockLevel
dijawab oleh spool menggunakan indeksnya. Ini berlaku untuk setiap iterasi penerapan, termasuk yang pertama. TransactionHistory
tabel hanya dipindai sekali.
Masukan yang diurutkan ke spool
Dimungkinkan untuk menerapkan input yang diurutkan ke spool indeks yang bersemangat, tetapi ini memengaruhi perkiraan biaya, seperti yang disebutkan dalam pendahuluan. Untuk contoh di atas, mengaktifkan tanda jejak tidak berdokumen akan menghasilkan rencana tanpa gulungan:
SELECT P.ProductID, P.[Name], P.SafetyStockLevel, TH.Quantity FROM Production.Product AS P CROSS APPLY ( SELECT MAX(TH.Quantity) FROM Production.TransactionHistory AS TH WHERE TH.ProductID = P.ProductID AND TH.Quantity < P.SafetyStockLevel GROUP BY () ) AS TH (Quantity) WHERE P.[Name] LIKE N'A%' OPTION (QUERYTRACEON 9260);
Perkiraan biaya Pencarian Indeks . ini dan Pencarian Kunci paketnya adalah 3.11631 unit. Ini lebih dari biaya paket dengan spool indeks saja, tetapi lebih murah dari paket dengan spool indeks dan input yang diurutkan.
Untuk melihat rencana dengan input yang diurutkan ke spool, kita perlu meningkatkan jumlah iterasi loop yang diharapkan. Ini memberi spool kesempatan untuk membayar biaya tambahan Urutkan . Salah satu cara untuk memperluas jumlah baris yang diharapkan dari Product
tabel adalah untuk membuat Name
predikat kurang restriktif:
SELECT P.ProductID, P.[Name], P.SafetyStockLevel, TH.Quantity FROM Production.Product AS P CROSS APPLY ( SELECT MAX(TH.Quantity) FROM Production.TransactionHistory AS TH WHERE TH.ProductID = P.ProductID AND TH.Quantity < P.SafetyStockLevel GROUP BY () ) AS TH (Quantity) WHERE P.[Name] LIKE N'[A-P]%' OPTION (QUERYTRACEON 9260);
Ini memberi kita rencana eksekusi dengan input yang diurutkan ke spool:
JoinToIndexOnTheFly
Aturan ini mengubah gabungan dalam ke lamar , dengan spool indeks yang menarik di sisi dalam. Setidaknya salah satu predikat gabungan harus berupa pertidaksamaan agar aturan ini dapat dicocokkan.
Ini adalah aturan yang jauh lebih khusus daripada SelToIndexOnTheFly
, tapi idenya hampir sama. Dalam hal ini, seleksi (predikat) yang ditransformasikan ke pencarian spool indeks dikaitkan dengan gabungan. Transformasi dari bergabung menjadi menerapkan memungkinkan predikat join dipindahkan dari join itu sendiri ke sisi dalam dari apply.
Demo
SELECT P.ProductID, P.[Name], P.SafetyStockLevel, Quantity = MAX(TH.Quantity) FROM Production.Product AS P JOIN Production.TransactionHistory AS TH ON TH.ProductID = P.ProductID AND TH.Quantity < P.SafetyStockLevel WHERE P.[Name] LIKE N'[A-P]%' GROUP BY P.ProductID, P.[Name], P.SafetyStockLevel OPTION (LOOP JOIN);
Seperti sebelumnya, kami dapat meminta input yang diurutkan ke spool:
SELECT P.ProductID, P.[Name], P.SafetyStockLevel, Quantity = MAX(TH.Quantity) FROM Production.Product AS P JOIN Production.TransactionHistory AS TH ON TH.ProductID = P.ProductID AND TH.Quantity < P.SafetyStockLevel WHERE P.[Name] LIKE N'[A-P]%' GROUP BY P.ProductID, P.[Name], P.SafetyStockLevel OPTION (LOOP JOIN, QUERYTRACEON 9260);
Kali ini, biaya penyortiran ekstra telah mendorong pengoptimal untuk memilih paket paralel.
Efek samping yang tidak diinginkan adalah Urutkan operator tumpah ke tempdb . Total pemberian memori yang tersedia untuk penyortiran cukup, tetapi dibagi rata antara utas paralel (seperti biasa). Seperti disebutkan dalam pendahuluan, SQL Server tidak mendukung penyisipan paralel ke indeks b-tree, sehingga operator di bawah spool indeks bersemangat berjalan pada satu utas. Utas tunggal ini hanya mendapat sebagian kecil dari pemberian memori, jadi Urutkan tumpah ke tempdb .
Efek samping ini mungkin salah satu alasan tanda jejak tidak didokumentasikan dan tidak didukung.
SelSTVFToIdxOnFly
Aturan ini melakukan hal yang sama seperti SelToIndexOnTheFly
, tetapi untuk fungsi bernilai tabel streaming (sTVF) sumber baris. sTVF ini digunakan secara luas secara internal untuk mengimplementasikan DMV dan DMF antara lain. Mereka muncul dalam rencana eksekusi modern sebagai Fungsi Bernilai Tabel operator (awalnya sebagai pemindaian tabel jarak jauh ).
Di masa lalu, banyak dari sTVF ini tidak dapat menerima parameter yang berkorelasi dari aplikasi. Mereka dapat menerima literal, variabel, dan parameter modul, hanya saja tidak berlaku referensi luar. Masih ada peringatan tentang ini dalam dokumentasi, tetapi sekarang sudah agak ketinggalan zaman.
Bagaimanapun, intinya adalah terkadang SQL Server tidak dapat melewati apply referensi luar sebagai parameter untuk sTVF. Dalam situasi itu, masuk akal untuk mewujudkan bagian dari hasil sTVF dalam spool indeks yang bersemangat. Aturan saat ini memberikan kemampuan itu.
Demo
Contoh kode berikutnya menunjukkan kueri DMV yang berhasil dikonversi dari gabungan menjadi menerapkan . Referensi luar diteruskan sebagai parameter ke DMV kedua:
-- Transformed to an apply -- Outer reference passed as a parameter SELECT DES.session_id, DES.login_time, DESWS.waiting_tasks_count FROM sys.dm_exec_sessions AS DES JOIN sys.dm_exec_session_wait_stats AS DESWS ON DESWS.session_id = DES.session_id OPTION (FORCE ORDER);
Properti rencana dari statistik tunggu TVF menunjukkan parameter input. Nilai parameter kedua diberikan sebagai referensi luar dari sesi DMV:
Sayang sekali sys.dm_exec_session_wait_stats
adalah tampilan, bukan fungsi, karena itu mencegah kita menulis berlaku secara langsung.
Penulisan ulang di bawah ini sudah cukup untuk mengalahkan konversi internal:
-- Rewrite to avoid TVF parameter trickery SELECT DES.session_id, DES.login_time, DESWS.waiting_tasks_count FROM sys.dm_exec_sessions AS DES JOIN sys.dm_exec_session_wait_stats AS DESWS ON DESWS.session_id >= DES.session_id AND DESWS.session_id <= DES.session_id OPTION (FORCE ORDER);
Dengan session_id
predikat sekarang tidak digunakan sebagai parameter, SelSTVFToIdxOnFly
aturan bebas untuk mengonversinya menjadi spool indeks yang bersemangat:
Saya tidak ingin meninggalkan kesan bahwa penulisan ulang yang rumit diperlukan untuk mendapatkan spool indeks yang bersemangat melalui sumber DMV – itu hanya membuat demo lebih mudah. Jika Anda menemukan kueri dengan gabungan DMV yang menghasilkan rencana dengan gulungan yang bersemangat, setidaknya Anda tahu bagaimana hal itu terjadi.
Anda tidak dapat membuat indeks pada DMV, jadi Anda mungkin perlu menggunakan hash atau gabungan gabungan jika rencana eksekusi tidak berjalan cukup baik.
CTE rekursif
Dua aturan yang tersisa adalah SelIterToIdxOnFly
dan JoinIterToIdxOnFly
. Mereka adalah rekan langsung dari SelToIndexOnTheFly
dan JoinToIndexOnTheFly
untuk sumber data CTE rekursif. Ini sangat jarang dalam pengalaman saya, jadi saya tidak akan memberikan demo untuk mereka. (Sama seperti Iter
bagian dari nama aturan masuk akal:Itu berasal dari fakta bahwa SQL Server mengimplementasikan rekursi ekor sebagai iterasi bersarang.)
Ketika CTE rekursif direferensikan beberapa kali di bagian dalam aplikasi, aturan yang berbeda (SpoolOnIterator
) dapat menyimpan hasil CTE:
WITH R AS ( SELECT 1 AS n UNION ALL SELECT R.n + 1 FROM R WHERE R.n < 10 ) SELECT R1.n FROM R AS R1 CROSS JOIN R AS R2;
Rencana eksekusi menampilkan Eager Row Count Spool yang langka :
Pemikiran Terakhir
Gulungan indeks yang bersemangat sering kali merupakan tanda bahwa indeks permanen yang berguna hilang dari skema basis data. Hal ini tidak selalu terjadi, seperti yang ditunjukkan oleh contoh fungsi bernilai tabel streaming.