Mengapa taksiran baris SQL Server berubah ketika saya menambahkan petunjuk bergabung?


15

Saya memiliki kueri yang bergabung dengan beberapa tabel dan berkinerja sangat buruk - perkiraan baris adalah cara (1000 kali) mati dan Nested Loops bergabung dipilih, menghasilkan beberapa pemindaian tabel. Bentuk kueri cukup mudah, terlihat seperti ini:

SELECT t1.id
FROM t1
INNER JOIN t2 ON t1.id = t2.t1_id
LEFT OUTER JOIN t3 ON t2.id = t3.t2_id
LEFT OUTER JOIN t4 ON t3.t4_id = t4.id 
WHERE t4.id = some_GUID

Bermain-main dengan kueri, saya perhatikan bahwa ketika saya mengisyaratkan untuk menggunakan gabungan Gabung untuk salah satu gabungan, itu berjalan beberapa kali lebih cepat. Ini bisa saya mengerti - Gabung bergabung adalah pilihan yang lebih baik untuk data yang bergabung, tetapi SQL Server hanya tidak memperkirakannya dengan benar memilih Nested Loops.

Apa yang saya tidak sepenuhnya mengerti adalah mengapa petunjuk ini mengubah semua perkiraan untuk semua operator paket? Dari membaca berbagai artikel dan buku, saya berasumsi bahwa estimasi kardinalitas dilakukan sebelum rencana dibuat, jadi menggunakan petunjuk tidak akan mengubah estimasi, tetapi lebih tepatnya memberitahu SQL Server untuk menggunakan implementasi join fisik tertentu.

Apa yang saya lihat, bagaimanapun, adalah bahwa Merge hint menyebabkan semua estimasi menjadi sangat sempurna. Mengapa ini terjadi dan apakah ada teknik umum untuk membuat pengoptimal kueri membuat perkiraan yang lebih baik tanpa petunjuk - mengingat statistik jelas memungkinkan untuk ini?

UPD: rencana eksekusi yang dianonimkan dapat ditemukan di sini: https://www.dropbox.com/s/hchfuru35qqj89s/merge_join.sqlplan?dl=0 https://www.dropbox.com/s/38sjtv0t7vjjfdp/no_hints_join.sqlplan?dl = 0

Saya memeriksa statistik yang digunakan oleh kedua pertanyaan menggunakan TF 3604, 9292 dan 9204, dan itu identik. Namun indeks yang dipindai / dicari berbeda di antara kueri.

Selain itu, saya mencoba menjalankan kueri dengan OPTION (FORCE ORDER)- itu berjalan lebih cepat daripada menggunakan gabungan bergabung, memilih HASH MATCH untuk setiap bergabung.


3
Pernahkah Anda memperhatikan bahwa Anda memiliki gabungan luar tetapi Anda kemudian menggunakan tabel di klausa mana?
James Z

@ JamesZ - ya, saya tahu itu, saya tidak berpikir ada masalah dengan itu.
Alexander Shelemin

9
@AlexSh Nah, ada masalah logis / semantik dengan itu, karena itu mengubah bagian luar Anda bergabung menjadi bagian dalam.
Aaron Bertrand

Jawaban:


21

Dari membaca berbagai artikel dan buku, saya berasumsi bahwa perkiraan kardinalitas dilakukan sebelum rencana itu dibuat.

Tidak persis. Sebuah perkiraan kardinalitas awal berasal (setelah penyederhanaan dan pekerjaan lain), yang mempengaruhi pesanan awal bergabung dipilih oleh optimizer.

Namun, eksplorasi selanjutnya (selama optimasi berbasis biaya) dapat, dan sering dilakukan, menghasilkan estimasi kardinalitas baru yang dihitung. CE yang belakangan ini mungkin lebih atau kurang 'akurat'. Jika hasil di bawah perkiraan, pengoptimal dapat memilih paket yang terlihat lebih murah, tetapi kenyataannya berjalan lebih lama.

Secara umum, tidak ada jaminan bahwa estimasi kardinalitas untuk subtree semantik identik akan menghasilkan hasil yang sama. Bagaimanapun, ini adalah proses statistik, dan beberapa operasi memiliki dukungan CE yang lebih dalam daripada yang lain.

Dalam kasus Anda, tampaknya ada faktor lain - pengoptimal memperkenalkan (atau bergerak) Top, yang menetapkan tujuan baris pada subtree di bawahnya:

Rencanakan fragmen

Jika Anda mengaktifkan jejak flag 4138 (pada 2008 R2 atau lebih baru), Anda dapat menemukan perkiraan lebih sesuai dengan harapan, atau bahkan pengoptimal tidak akan lagi memilih loop bersarang.

Apa yang saya lihat, bagaimanapun, adalah bahwa Merge hint menyebabkan semua estimasi menjadi sangat sempurna.

Ada unsur keberuntungan yang terlibat di sini. Orang-orang cenderung menulis pertanyaan, atau setidaknya gabungan, dalam urutan yang mereka harapkan dilakukan secara fisik. Menggunakan petunjuk bergabung datang dengan tersirat FORCE ORDER, sehingga memperbaiki urutan bergabung untuk mencocokkan dengan bentuk tekstual, dan mematikan banyak aturan eksplorasi pengoptimal yang dapat menyebabkan estimasi ulang kardinalitas.

Selain itu, saya mencoba menjalankan kueri dengan OPTION (FORCE ORDER)- itu berjalan lebih cepat daripada menggunakan gabungan bergabung, memilih HASH MATCH untuk setiap bergabung.

Ini sama dengan mengisyaratkan bergabung, tetapi tidak membatasi pilihan operator bergabung fisik. Sekali lagi, jika Anda menulis permintaan bergabung dengan urutan dengan cara yang logis, sangat mungkin Anda akan mendapatkan rencana yang masuk akal. Tentu saja, Anda kehilangan banyak kemampuan pengoptimal dengan cara ini, yang mungkin tidak menghasilkan hasil yang optimal dalam situasi yang lebih umum.

Anda mungkin tidak ingin menggunakan FORCE ORDERterlalu sering karena itu adalah petunjuk yang sangat kuat (direktif) yang memiliki efek lebih luas daripada memaksa urutan bergabung; misalnya, mencegah agregat bergerak pengoptimal dan memperkenalkan agregat parsial. Saya sangat menyarankan untuk tidak menggunakan petunjuk ini kecuali dalam keadaan yang sangat luar biasa, dan oleh tuner yang benar-benar ahli .

Analisis terperinci akan membutuhkan lebih banyak waktu daripada yang saya miliki saat ini, dan akses ke salinan database yang hanya statistik.


-10

Di mana meniadakan kiri.
Mengapa menyulitkan pengoptimal?
Pada 3 atau lebih bergabung dengan optimizer akan Cenderung defensif dan menjadi loop bergabung karena yang melindungi memori
Sebuah atau kondisi dalam bergabung itu juga akan cenderung masuk ke loop bergabung - apakah saya punya bukti kuat itu akan terjadi setiap kali - tidak - masih kenyataan
Dengan banyak gabung menarik kondisi dari mana ke gabung ketika Anda bisa

SELECT t1.id
  FROM t1
  JOIN t2 
        ON t1.id = t2.t1_id
  JOIN t3 
        ON t2.id = t3.t2_id
  JOIN t4 
        ON t3.t4_id = t4.id 
       AND t4.id = some_GUID 

Atau bahkan lebih baik lagi - saya yakin ini akan memenuhi atau mengalahkan petunjuk atau kekuatan Anda

SELECT t1.id
  FROM t1
  JOIN t2 
        ON t1.id = t2.t1_id
  JOIN t3 
        ON t2.id = t3.t2_id
       AND t3.t4_id = some_GUID

Masalah dengan petunjuk adalah mereka untuk data dalam keadaan tertentu. Tulis kueri bersih dan biarkan pengoptimal melakukan tugasnya. Beberapa kali itu hanya membutuhkan lebih banyak statistik untuk melakukan hal yang benar tetapi kemudian akan terkunci.

Mengapa estimasi berbeda. Rencana yang berbeda. Mulailah dengan pertanyaan yang memberi peluang pengoptimal kesempatan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.