Mengapa estimasi biaya (sama) 1000 mencari indeks unik berbeda dalam rencana ini?

28

Dalam kueri di bawah ini, kedua rencana eksekusi diperkirakan akan melakukan 1.000 pencarian pada indeks yang unik.

Pencarian didorong oleh pemindaian terurut pada tabel sumber yang sama sehingga tampaknya harus mencari nilai yang sama dalam urutan yang sama.

Kedua loop bersarang miliki <NestedLoops Optimized="false" WithOrderedPrefetch="true">

Adakah yang tahu mengapa tugas ini dihitung biayanya sebesar 0,172434 dalam rencana pertama tetapi 3,01702 di yang kedua?

(Alasan untuk pertanyaan ini adalah bahwa kueri pertama disarankan kepada saya sebagai optimasi karena biaya rencana yang jauh lebih rendah. Sebenarnya itu terlihat bagi saya seolah-olah ia bekerja lebih banyak tetapi saya hanya mencoba menjelaskan perbedaannya .. .)

Mendirikan

CREATE TABLE dbo.Target(KeyCol int PRIMARY KEY, OtherCol char(32) NOT NULL);

CREATE TABLE dbo.Staging(KeyCol int PRIMARY KEY, OtherCol char(32) NOT NULL); 

INSERT INTO dbo.Target
SELECT TOP (1000000) ROW_NUMBER() OVER (ORDER BY @@SPID), LEFT(NEWID(),32)
FROM master..spt_values v1,  
     master..spt_values v2;

INSERT INTO dbo.Staging
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY @@SPID), LEFT(NEWID(),32)
FROM master..spt_values v1;

Pertanyaan 1 tautan "Tempel paket"

WITH T
     AS (SELECT *
         FROM   Target AS T
         WHERE  T.KeyCol IN (SELECT S.KeyCol
                             FROM   Staging AS S))
MERGE T
USING Staging S
ON ( T.KeyCol = S.KeyCol )
WHEN NOT MATCHED THEN
  INSERT ( KeyCol, OtherCol )
  VALUES(S.KeyCol, S.OtherCol )
WHEN MATCHED AND T.OtherCol > S.OtherCol THEN
  UPDATE SET T.OtherCol = S.OtherCol;

Kueri 2 tautan "Tempel paket"

MERGE Target T
USING Staging S
ON ( T.KeyCol = S.KeyCol )
WHEN NOT MATCHED THEN
  INSERT ( KeyCol, OtherCol )
  VALUES( S.KeyCol, S.OtherCol )
WHEN MATCHED AND T.OtherCol > S.OtherCol THEN
  UPDATE SET T.OtherCol = S.OtherCol; 

Pertanyaan 1

Pertanyaan 2

Di atas diuji pada SQL Server 2014 (SP2) (KB3171021) - 12.0.5000.0 (X64)


@ Jo Obbish menunjukkan dalam komentar bahwa repro yang lebih sederhana akan

SELECT *
FROM staging AS S 
  LEFT OUTER JOIN Target AS T 
    ON T.KeyCol = S.KeyCol;

vs.

SELECT *
FROM staging AS S 
  LEFT OUTER JOIN (SELECT * FROM Target) AS T 
    ON T.KeyCol = S.KeyCol;

Untuk tabel pementasan 1.000 baris kedua di atas masih memiliki bentuk rencana yang sama dengan loop bersarang dan rencana tanpa tabel turunan nampak lebih murah, tetapi untuk tabel pementasan 10.000 baris dan tabel target yang sama seperti di atas perbedaan biaya tidak mengubah rencana bentuk (dengan pemindaian penuh dan gabungan bergabung yang tampaknya relatif lebih menarik daripada upaya berbiaya mahal) menunjukkan perbedaan biaya ini dapat memiliki implikasi selain hanya membuat lebih sulit untuk membandingkan rencana.

masukkan deskripsi gambar di sini

Martin Smith
sumber

Jawaban:

21

Adakah yang tahu mengapa tugas ini dihitung biayanya sebesar 0,172434 dalam rencana pertama tetapi 3,01702 di yang kedua?

Secara umum, sisi dalam mencari di bawah simpul bergabung bergabung dihitung dengan asumsi pola I / O acak. Ada pengurangan berbasis penggantian sederhana untuk akses selanjutnya, memperhitungkan kemungkinan bahwa halaman yang diperlukan telah dibawa ke dalam memori oleh iterasi sebelumnya. Penilaian dasar ini menghasilkan biaya standar (lebih tinggi).

Ada input penetapan biaya lain, Smart Seek Costing , yang sedikit diketahui detailnya. Dugaan saya (dan hanya itu yang ada pada tahap ini) adalah bahwa SSC berupaya menilai sisi dalam mencari biaya I / O secara lebih terperinci, mungkin dengan mempertimbangkan pemesanan lokal dan / atau kisaran nilai yang harus diambil. Siapa tahu.

Misalnya, operasi pencarian pertama membawa tidak hanya baris yang diminta, tetapi semua baris pada halaman itu (dalam urutan indeks). Dengan pola akses keseluruhan, mengambil 1000 baris dalam 1000 pencarian hanya membutuhkan 2 pembacaan fisik, bahkan dengan read-ahead dan prefetching dinonaktifkan. Dari perspektif itu, penetapan biaya I / O default merupakan perkiraan yang terlalu tinggi, dan biaya yang disesuaikan dengan SSC lebih dekat dengan kenyataan.

Tampaknya masuk akal untuk mengharapkan bahwa SSC akan paling efektif di mana loop menggerakkan indeks mencari lebih atau kurang secara langsung, dan referensi luar gabungan adalah dasar dari operasi pencarian. Dari apa yang dapat saya katakan, SSC selalu berusaha untuk operasi fisik yang sesuai, tetapi paling sering tidak menghasilkan penyesuaian ke bawah ketika pencarian dipisahkan dari bergabung dengan operasi lain. Filter sederhana adalah satu pengecualian untuk ini, mungkin karena SQL Server sering dapat mendorong ini ke operator akses data. Bagaimanapun, pengoptimal memiliki dukungan yang cukup mendalam untuk pemilihan.

Sangat disayangkan bahwa Compute Scalar untuk proyeksi luar subquery tampaknya mengganggu SSC di sini. Compute Scalars biasanya dipindahkan di atas join, tetapi yang ini harus tetap di tempatnya. Meski begitu, kebanyakan Compute Scalars yang normal cukup transparan untuk pengoptimalan, jadi ini agak mengejutkan.

Apapun, ketika operasi fisik PhyOp_Rangedihasilkan dari seleksi sederhana pada indeks SelIdxToRng, SSC efektif. Ketika semakin kompleks SelToIdxStrategy(pemilihan pada tabel untuk strategi indeks) digunakan, hasilnya PhyOp_Rangemenjalankan SSC tetapi tidak menghasilkan pengurangan. Sekali lagi, tampaknya operasi yang lebih sederhana dan lebih langsung bekerja paling baik dengan SSC.

Saya berharap saya bisa memberi tahu Anda apa yang dilakukan SSC, dan menunjukkan perhitungan yang tepat, tetapi saya tidak tahu detailnya. Jika Anda ingin menjelajahi hasil jejak terbatas yang tersedia untuk Anda sendiri, Anda dapat menggunakan tanda jejak tidak berdokumen 2398. Contoh hasil adalah:

Biaya pencarian pintar (7.1) :: 1.34078e + 154, 0.001

Contoh itu terkait dengan memo grup 7, alternatif 1, menunjukkan batas atas biaya, dan faktor 0,001. Untuk melihat faktor-faktor yang lebih bersih, pastikan untuk membangun kembali tabel tanpa paralelisme sehingga halaman sepadat mungkin. Tanpa melakukan itu, faktornya lebih seperti 0,000821 untuk contoh tabel Target Anda. Tentu saja ada beberapa hubungan yang cukup jelas.

SSC juga dapat dinonaktifkan dengan bendera jejak tidak berdokumen 2399. Dengan bendera itu aktif, kedua biaya adalah nilai yang lebih tinggi.

Paul White mengatakan GoFundMonica
sumber
8

Tidak yakin ini adalah jawaban tetapi agak panjang untuk dikomentari. Penyebab perbedaannya adalah spekulasi murni di pihak saya dan mungkin bisa menjadi makanan untuk dipikirkan orang lain.

Pertanyaan yang disederhanakan dengan rencana eksekusi.

SELECT S.KeyCol, 
       S.OtherCol,
       T.*
FROM staging AS S 
  LEFT OUTER JOIN Target AS T 
    ON T.KeyCol = S.KeyCol;

SELECT S.KeyCol, 
       S.OtherCol,
       T.*
FROM staging AS S 
  LEFT OUTER JOIN (
                  SELECT *
                  FROM Target
                  ) AS T 
    ON T.KeyCol = S.KeyCol;

masukkan deskripsi gambar di sini

Perbedaan utama antara kueri setara ini yang benar-benar dapat menghasilkan rencana eksekusi yang identik adalah operator skalar komputasi. Saya tidak tahu mengapa itu harus ada di sana, tetapi saya kira itu sejauh optimizer dapat pergi untuk mengoptimalkan tabel turunan.

Dugaan saya adalah bahwa kehadiran skalar komputasi adalah apa yang mengumpulkan biaya IO untuk permintaan kedua.

Dari Di Dalam Pengoptimal: Perencanaan Biaya

Biaya CPU dihitung sebagai 0,0001581 untuk baris pertama, dan 0,000011 untuk baris berikutnya.
...
Biaya I / O 0,003125 tepat 1/320 - mencerminkan asumsi model bahwa subsistem disk dapat melakukan 320 operasi I / O acak per detik
...
komponen penetapan biaya cukup cerdas untuk mengenali bahwa jumlah total halaman yang perlu dibawa dari disk tidak pernah melebihi jumlah halaman yang dibutuhkan untuk menyimpan seluruh tabel.

Dalam kasus saya, tabel membutuhkan 5618 halaman dan untuk mendapatkan 1000 baris dari 1000000 baris perkiraan jumlah halaman yang dibutuhkan adalah 5,618 memberikan Biaya IO 0,015625.

Biaya CPU untuk kedua query seams sama 0.0001581 * 1000 executions = 0.1581,.

Jadi menurut artikel yang ditautkan di atas, kita dapat menghitung biaya untuk kueri pertama menjadi 0,173725.

Dan dengan asumsi saya benar tentang bagaimana skalar komputasi membuat berantakan Biaya IO dapat dihitung menjadi 3,2831.

Tidak persis apa yang ditunjukkan dalam rencana tetapi itu tepat di lingkungan.

Mikael Eriksson
sumber
6

(Ini akan lebih baik sebagai komentar atas jawaban Paul, tetapi saya belum memiliki cukup perwakilan.)

Saya ingin memberikan daftar bendera jejak (dan beberapa DBCCpernyataan) yang saya gunakan sampai pada kesimpulan terdekat, kalau-kalau akan membantu untuk menyelidiki perbedaan yang serupa di masa depan. Semua ini tidak boleh digunakan pada produksi .

Pertama, saya melihat pada Final Memo untuk melihat apa operator fisik yang digunakan. Mereka tentu terlihat sama sesuai dengan rencana eksekusi grafis. Jadi, saya menggunakan flag jejak 3604dan 8615, yang pertama mengarahkan output ke klien dan yang kedua mengungkapkan Memo Final:

SELECT S.*, T.KeyCol
FROM Staging AS S
      LEFT OUTER JOIN Target AS T
       ON T.KeyCol = S.KeyCol
OPTION(QUERYTRACEON 3604, -- Output client info
       QUERYTRACEON 8615, -- Shows Final Memo structure
       RECOMPILE);

Menelusuri kembali dari Root Group, saya menemukan PhyOp_Rangeoperator yang hampir identik ini :

  1. PhyOp_Range 1 ASC 2.0 Cost(RowGoal 0,ReW 0,ReB 999,Dist 1000,Total 1000)= 0.175559(Distance = 2)
  2. PhyOp_Range 1 ASC 3.0 Cost(RowGoal 0,ReW 0,ReB 999,Dist 1000,Total 1000)= 3.01702(Distance = 2)

Satu-satunya perbedaan yang jelas bagi saya adalah 2.0dan 3.0, yang merujuk pada masing-masing "grup memo 2, asli" dan "grup memo 3, asli". Memeriksa memo, ini merujuk pada hal yang sama - jadi tidak ada perbedaan yang terungkap.

Kedua, saya melihat ke seluruh bendera jejak yang terbukti sia-sia bagi saya - tetapi memiliki beberapa konten yang menarik. Saya mengangkat sebagian besar dari Benjamin Nevarez . Saya mencari petunjuk tentang aturan optimasi yang diterapkan dalam satu kasus dan bukan yang lain.

 SELECT S.*, T.KeyCol
 FROM Staging AS S
      LEFT OUTER JOIN Target AS T
        ON T.KeyCol = S.KeyCol
 OPTION (QUERYTRACEON 3604, -- Output info to client
         QUERYTRACEON 2363, -- Show stats and cardinality info
         QUERYTRACEON 8675, -- Show optimization process info
         QUERYTRACEON 8606, -- Show logical query trees
         QUERYTRACEON 8607, -- Show physical query tree
         QUERYTRACEON 2372, -- Show memory utilization info for optimization stages 
         QUERYTRACEON 2373, -- Show memory utilization info for applying rules
         RECOMPILE );

Ketiga, saya melihat aturan mana yang diterapkan untuk kita PhyOp_Rangeyang terlihat sangat mirip. Saya menggunakan beberapa tanda jejak yang disebutkan oleh Paul dalam sebuah posting blog .

SELECT S.*, T.KeyCol
FROM Staging AS S
      LEFT OUTER JOIN (SELECT KeyCol
                      FROM Target) AS T
       ON T.KeyCol = S.KeyCol
OPTION (QUERYTRACEON 3604, -- Output info to client
        QUERYTRACEON 8619, -- Show applied optimization rules
        QUERYTRACEON 8620, -- Show rule-to-memo info
        QUERYTRACEON 8621, -- Show resulting tree
        QUERYTRACEON 2398, -- Show "smart seek costing"
        RECOMPILE );

Dari output, kita melihat bahwa direct yang JOINditerapkan aturan ini untuk mendapatkan kita PhyOp_RangeOperator: Rule Result: group=7 2 <SelIdxToRng>PhyOp_Range 1 ASC 2 (Distance = 2). Subselect yang diterapkan aturan ini sebagai gantinya: Rule Result: group=9 2 <SelToIdxStrategy>PhyOp_Range 1 ASC 3 (Distance = 2). Ini juga tempat Anda melihat info "biaya pencarian pintar" yang terkait dengan setiap aturan. Untuk direct JOINini adalah output (untuk saya): Smart seek costing (7.2) :: 1.34078e+154 , 0.001. Untuk subselect, ini adalah output: Smart seek costing (9.2) :: 1.34078e+154 , 1.

Pada akhirnya, saya tidak bisa menyimpulkan banyak - tetapi jawaban Paul menutup sebagian besar celah. Saya ingin melihat lebih banyak info tentang biaya pencarian cerdas.

Steven Hibble
sumber
4

Ini juga bukan jawaban - seperti yang dicatat Mikael, sulit untuk membahas masalah ini di komentar ...

Menariknya, jika Anda mengubah subquery (select KeyCol FROM Target)menjadi TVF sebaris, Anda melihat paket, dan biayanya, sama dengan kueri asli yang sederhana:

CREATE FUNCTION dbo.cs_test()
RETURNS TABLE
WITH SCHEMABINDING
AS 
RETURN (
    SELECT KeyCol FROM dbo.Target
    );

/* "normal" variant */
SELECT S.KeyCol, s.OtherCol, T.KeyCol 
FROM staging AS S 
    LEFT OUTER JOIN Target AS T ON T.KeyCol = S.KeyCol;

/* "subquery" variant */
SELECT S.KeyCol, s.OtherCol, T.KeyCol 
FROM staging AS S 
    LEFT OUTER JOIN (SELECT KeyCol FROM Target) AS T ON T.KeyCol = S.KeyCol;

/* "inline-TVF" variant */
SELECT S.KeyCol, s.OtherCol, T.KeyCol 
FROM staging AS S 
    LEFT OUTER JOIN dbo.cs_test() t ON s.KeyCol = t.Keycol

Paket kueri ( tautan pastetheplan ):

masukkan deskripsi gambar di sini

Pengurangan membuat saya percaya bahwa mesin penetapan biaya bingung tentang dampak potensial dari jenis subquery ini .

Ambil contoh, berikut ini:

SELECT S.KeyCol, s.OtherCol, T.KeyCol 
FROM staging AS S 
    LEFT OUTER JOIN (
        SELECT KeyCol = CHECKSUM(NEWID()) 
        FROM Target
        ) AS T ON T.KeyCol = S.KeyCol;

Bagaimana Anda biaya itu? Pengoptimal kueri memilih paket yang sangat mirip dengan varian "subquery" di atas, yang mengandung skalar komputasi ( tautan pastetheplan.com ):

masukkan deskripsi gambar di sini

Komputasi skalar memiliki biaya yang sangat berbeda dari varian "subquery" yang ditunjukkan di atas, namun itu masih merupakan dugaan saja karena pengoptimal kueri tidak memiliki cara untuk mengetahui, apriori, berapa jumlah baris yang dikembalikan. Paket tersebut menggunakan pencocokan hash untuk gabungan luar kiri karena perkiraan baris tidak dapat diketahui dan karenanya ditetapkan ke jumlah baris dalam tabel Target.

masukkan deskripsi gambar di sini

Saya tidak memiliki kesimpulan yang bagus dari ini kecuali bahwa saya setuju dengan pekerjaan yang dilakukan Mikael dalam jawabannya, dan saya berharap orang lain dapat memberikan jawaban yang lebih baik.

Max Vernon
sumber