Mengeksekusi kueri dari sini untuk menarik acara kebuntuan dari sesi peristiwa diperpanjang standar
SELECT CAST (
REPLACE (
REPLACE (
XEventData.XEvent.value ('(data/value)[1]', 'varchar(max)'),
'<victim-list>', '<deadlock><victim-list>'),
'<process-list>', '</victim-list><process-list>')
AS XML) AS DeadlockGraph
FROM (SELECT CAST (target_data AS XML) AS TargetData
FROM sys.dm_xe_session_targets st
JOIN sys.dm_xe_sessions s ON s.address = st.event_session_address
WHERE [name] = 'system_health') AS Data
CROSS APPLY TargetData.nodes ('//RingBufferTarget/event') AS XEventData (XEvent)
WHERE XEventData.XEvent.value('@name', 'varchar(4000)') = 'xml_deadlock_report';
membutuhkan waktu sekitar 20 menit untuk menyelesaikan mesin saya. Statistik yang dilaporkan adalah
Table 'Worktable'. Scan count 0, logical reads 68121, physical reads 0, read-ahead reads 0,
lob logical reads 25674576, lob physical reads 0, lob read-ahead reads 4332386.
SQL Server Execution Times:
CPU time = 1241269 ms, elapsed time = 1244082 ms.
Jika saya menghapus WHERE
klausa itu selesai dalam waktu kurang dari satu detik mengembalikan 3,782 baris.
Demikian pula jika saya menambah OPTION (MAXDOP 1)
kueri asli yang mempercepat dengan statistik sekarang menunjukkan lebih sedikit lob dibaca.
Table 'Worktable'. Scan count 0, logical reads 15, physical reads 0, read-ahead reads 0,
lob logical reads 6767, lob physical reads 0, lob read-ahead reads 6076.
SQL Server Execution Times:
CPU time = 639 ms, elapsed time = 693 ms.
Jadi pertanyaan saya adalah
Adakah yang bisa menjelaskan apa yang terjadi? Mengapa rencana awal begitu buruk dan apakah ada cara yang dapat diandalkan untuk menghindari masalah?
Tambahan:
Saya juga menemukan bahwa mengubah kueri untuk INNER HASH JOIN
meningkatkan hal-hal sampai batas tertentu (tapi masih membutuhkan> 3 menit) karena hasil DMV sangat kecil, saya ragu bahwa tipe Bergabung itu sendiri yang bertanggung jawab dan menganggap ada sesuatu yang lain harus berubah. Statistik untuk itu
Table 'Worktable'. Scan count 0, logical reads 30294, physical reads 0, read-ahead reads 0,
lob logical reads 10741863, lob physical reads 0, lob read-ahead reads 4361042.
SQL Server Execution Times:
CPU time = 200914 ms, elapsed time = 203614 ms.
Setelah mengisi buffer cincin peristiwa diperpanjang ( DATALENGTH
dari XML
4.880.045 byte dan berisi 1.448 peristiwa.) Dan menguji versi pengurangan dari permintaan asli dengan dan tanpa MAXDOP
petunjuk.
SELECT COUNT(*)
FROM (SELECT CAST (target_data AS XML) AS TargetData
FROM sys.dm_xe_session_targets st
JOIN sys.dm_xe_sessions s
ON s.address = st.event_session_address
WHERE [name] = 'system_health') AS Data
CROSS APPLY TargetData.nodes ('//RingBufferTarget/event') AS XEventData (XEvent)
WHERE XEventData.XEvent.value('@name', 'varchar(4000)') = 'xml_deadlock_report'
SELECT*
FROM sys.dm_db_task_space_usage
WHERE session_id = @@SPID
Memberi hasil berikut
+-------------------------------------+------+----------+
| | Fast | Slow |
+-------------------------------------+------+----------+
| internal_objects_alloc_page_count | 616 | 1761272 |
| internal_objects_dealloc_page_count | 616 | 1761272 |
| elapsed time (ms) | 428 | 398481 |
| lob logical reads | 8390 | 12784196 |
+-------------------------------------+------+----------+
Ada perbedaan yang jelas dalam alokasi tempdb dengan yang lebih cepat menunjukkan 616
halaman yang dialokasikan dan tidak dialokasikan. Ini adalah jumlah halaman yang sama yang digunakan ketika XML dimasukkan ke dalam variabel juga.
Untuk rencana lambat, jumlah alokasi halaman ini menjadi jutaan. Polling dm_db_task_space_usage
sementara kueri sedang berjalan menunjukkan sepertinya terus-menerus mengalokasikan dan membatalkan alokasi halaman di tempdb
mana saja antara 1.800 dan 3.000 halaman dialokasikan pada satu waktu.
sumber
WHERE
klausa ke ekspresi XQuery; logika tidak harus dihapus untuk itu untuk pergi cepat:TargetData.nodes ('RingBufferTarget[1]/event[@name = "xml_deadlock_report"]')
. Yang mengatakan, saya tidak tahu XML internal cukup baik untuk menjawab pertanyaan yang Anda ajukan.Jawaban:
Alasan untuk perbedaan kinerja terletak pada bagaimana ekspresi skalar ditangani di mesin eksekusi. Dalam hal ini, ungkapan minat adalah:
Label ekspresi ini didefinisikan oleh operator Compute Scalar (simpul 11 dalam rencana seri, simpul 13 dalam rencana paralel). Compute Scalar operator berbeda dari operator lain (SQL Server 2005 dan seterusnya) dalam ekspresi yang mereka tetapkan tidak perlu dievaluasi pada posisi mereka muncul dalam rencana eksekusi yang terlihat; evaluasi dapat ditunda sampai hasil perhitungan diperlukan oleh operator yang lebih baru.
Dalam kueri ini,
target_data
string biasanya besar, membuat konversi dari string menjadiXML
mahal. Dalam rencana lambat, string keXML
konversi dilakukan setiap kali operator selanjutnya yang membutuhkan hasilExpr1000
rebound.Rebinding terjadi pada sisi dalam loop bersarang bergabung ketika parameter berkorelasi (referensi luar) berubah.
Expr1000
adalah referensi luar untuk sebagian besar loop bersarang bergabung dalam rencana eksekusi ini. Ekspresi direferensikan beberapa kali oleh beberapa Pembaca XML, Agregat Stream, dan oleh Filter start-up. Bergantung pada ukuranXML
, berapa kali string dikonversi keXML
dapat dengan mudah dalam jutaan.Tumpukan panggilan di bawah ini menunjukkan contoh
target_data
string yang akan dikonversiXML
(ConvertStringToXMLForES
- di mana ES adalah Layanan Ekspresi ):Filter Penyalaan
Pembaca XML (Streaming TVF secara internal)
Agregat Aliran
Mengubah string ke
XML
setiap kali salah satu operator ini menjelaskan perbedaan kinerja yang diamati dengan rencana loop bersarang. Ini terlepas dari apakah paralelisme digunakan atau tidak. Kebetulan pengoptimal memilih hash bergabung ketikaMAXDOP 1
petunjuk ditentukan. JikaMAXDOP 1, LOOP JOIN
ditentukan, kinerja buruk seperti halnya rencana paralel default (di mana pengoptimal memilih loop bersarang).Berapa banyak peningkatan kinerja dengan hash join tergantung pada apakah
Expr1000
muncul di sisi build atau probe operator. Kueri berikut menempatkan ekspresi di sisi probe:Saya telah membalik urutan tertulis gabungan dari versi yang ditunjukkan dalam pertanyaan, karena petunjuk gabungan (di
INNER HASH JOIN
atas) juga memaksa urutan untuk seluruh kueri, sama seperti jikaFORCE ORDER
telah ditentukan. Pembalikan diperlukan untuk memastikanExpr1000
muncul di sisi probe. Bagian yang menarik dari rencana eksekusi adalah:Dengan ekspresi yang ditentukan di sisi probe, nilainya di-cache:
Evaluasi
Expr1000
masih ditunda sampai operator pertama membutuhkan nilai (filter start-up dalam jejak stack di atas) tetapi nilai yang dihitung di-cache (CValHashCachedSwitch
) dan digunakan kembali untuk panggilan selanjutnya oleh Pembaca XML dan Agregat Stream. Jejak tumpukan di bawah ini menunjukkan contoh nilai cache yang digunakan kembali oleh Pembaca XML.Ketika perintah gabungan dipaksa sedemikian rupa sehingga definisi
Expr1000
terjadi pada sisi build hash bergabung, situasinya berbeda:Gabung hash membaca input build sepenuhnya untuk membuat tabel hash sebelum mulai mencari kecocokan. Akibatnya, kita harus menyimpan semua nilai, bukan hanya satu per utas yang sedang dikerjakan dari sisi penyelidikan paket. Karenanya, hash join menggunakan
tempdb
tabel kerja untuk menyimpanXML
data, dan setiap akses ke hasilExpr1000
oleh operator yang kemudian membutuhkan perjalanan yang mahal ketempdb
:Berikut ini menunjukkan rincian lebih lanjut dari jalur akses lambat:
Jika gabungan bergabung dipaksa, baris input diurutkan (operasi pemblokiran, seperti input build ke hash bergabung) menghasilkan pengaturan yang sama di mana akses lambat melalui
tempdb
meja kerja yang dioptimalkan diurutkan diperlukan karena ukuran data.Paket yang memanipulasi item data besar bisa menjadi masalah karena segala macam alasan yang tidak terlihat dari rencana eksekusi. Menggunakan hash join (dengan ekspresi pada input yang benar) bukanlah solusi yang baik. Itu bergantung pada perilaku internal tidak berdokumen tanpa jaminan itu akan bekerja dengan cara yang sama minggu depan, atau pada permintaan yang sedikit berbeda.
Pesannya adalah
XML
manipulasi dapat menjadi hal rumit untuk dioptimalkan hari ini. MenuliskanXML
ke variabel atau tabel sementara sebelum memotong-motong adalah solusi yang jauh lebih solid daripada apa pun yang ditunjukkan di atas. Salah satu cara untuk melakukan ini adalah:Akhirnya, saya hanya ingin menambahkan grafik Martin yang sangat bagus dari komentar di bawah ini:
sumber
@@IEAAXPEA_K
muncul.Itulah kode dari artikel saya yang semula diposting di sini:
http://www.sqlservercentral.com/articles/deadlock/65658/
Jika Anda membaca komentar, Anda akan menemukan beberapa alternatif yang tidak memiliki masalah kinerja yang Anda alami, satu menggunakan modifikasi dari permintaan asli itu, dan yang lainnya menggunakan variabel untuk menahan XML sebelum memprosesnya yang berhasil lebih baik. (Lihat komentar saya di Halaman 2) XML dari DMV bisa lambat untuk diproses, seperti juga dapat mem-parsing XML dari DMF untuk target file yang sering lebih baik dicapai dengan membaca data ke tabel temp terlebih dahulu dan kemudian memprosesnya. XML dalam SQL lambat dibandingkan dengan menggunakan hal-hal seperti .NET atau SQLCLR.
sumber
303 ms
dan3249 lob reads
. Pada 2012 saya juga perlu menambahkanand target_name='ring_buffer'
versi itu karena sepertinya ada dua target sekarang. Saya masih mencoba untuk mendapatkan gambaran mental tentang apa sebenarnya yang dilakukan dalam versi 20 menit.