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

Spool Indeks Eager dan Pengoptimal

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 dan SafetyStockLevel dari Product 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 dan TH.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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Pentingnya Memilih Ukuran Azure VM yang Tepat

  2. Penyembunyian Data dalam Aplikasi DB

  3. SQL PILIH KE Pernyataan

  4. Konten Tidak Terstruktur:Sumber Bahan Bakar yang Belum Dimanfaatkan untuk AI dan Pembelajaran Mesin

  5. Dasar-dasar ekspresi tabel, Bagian 8 – CTE, pertimbangan pengoptimalan lanjutan