XML okuyucularla planları optimize etme


34

Yürütülmesi buradan sorgu varsayılan genişletilmiş olaylar oturumun dışarı çıkmaz olaylarını çekmeye

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';

makineme tamamlamak için yaklaşık 20 dakika sürer. Rapor edilen istatistikler

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.

Yavaş Plan XML

Paralel

WHEREMaddeyi kaldırırsam, geri dönen 3,782 satır döndüren bir saniyeden daha kısa sürede tamamlanır.

Benzer şekilde OPTION (MAXDOP 1), şimdi çok daha az lob okuma gösteren istatistikler ile işleri hızlandıran orijinal sorguyu eklersem.

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.

Daha Hızlı Plan XML

Seri

Yani benim sorum

Neler olduğunu açıklayan var mı? Orijinal plan neden bu kadar feci bir şekilde daha kötü ve bu problemden kaçınmanın güvenilir bir yolu var mı?

İlave:

Ayrıca, sorguyu INNER HASH JOINbir dereceye kadar iyileştirmek için sorguyu değiştirmenin bir dereceye kadar iyileştirdiğini buldum (ancak> 3 dakika sürer), DMV sonuçları çok küçük olduğundan, Join türünün kendisinin sorumlu olduğundan ve başka bir şeyin değişmiş olması gerektiğini düşündüğünden şüpheliyim. Bunun için İstatistikleri

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.

(Ve plan)

(Genişletilmiş etkinlik halka tamponu doldurduktan sonra DATALENGTHbir XML4.880.045 bayt olduğunu ve 1.448 olayları ihtiva etmiştir.) İle ve olmadan orijinal sorgu versiyonu basılı bir kesim test MAXDOPipucu.

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 

Aşağıdaki sonuçları verdi

+-------------------------------------+------+----------+
|                                     | 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 |
+-------------------------------------+------+----------+

Tempdb tahsislerinde belirgin bir fark var. Daha hızlı olanı gösterilen 616sayfalar tahsis edildi ve tahsis edildi. Bu, XML de bir değişkene alındığında kullanılan sayfaların aynı miktarıdır.

Yavaş plan için bu sayfa tahsisi sayımları milyonlarca. Sorgu dm_db_task_space_usageçalışırken, sorgulama, tempdbsayfaların herhangi bir zamanda tahsis edilen 1.800 ila 3.000 sayfa arasında herhangi bir yerde sürekli olarak tahsis edildiğini ve tahsis edildiğini gösteriyor.


Yan WHEREtümceyi XQuery ifadesine taşıyabilirsiniz ; hızlı gitmek için mantık kaldırılacak zorunda değildir: TargetData.nodes ('RingBufferTarget[1]/event[@name = "xml_deadlock_report"]'). Bununla birlikte, XML içini, sorduğunuz soruyu cevaplayacak kadar iyi tanımıyorum.
Jon Seigel

Sayfalama @SQLPoolBoy sizin için Martin ... burada daha etkili önerileri olan ( yukarıdaki kodun kaynak makalesine dayanıyorlar) yaptığı yorumları incelemeyi önerdi .
Aaron Bertrand

Yanıtlar:


36

Performans farkının nedeni, yürütme motorunda skaler ifadelerin nasıl ele alındığına bağlıdır. Bu durumda, ilgi ifadesi şöyledir:

[Expr1000] = CONVERT(xml,DM_XE_SESSION_TARGETS.[target_data],0)

Bu ifade etiketi bir Hesaplama Skaler işleci tarafından tanımlanır (seri plandaki düğüm 11, paralel plandaki düğüm 13). Hesaplama Skaler işleçleri diğer işleçlerden farklıdır (SQL Server 2005 ve sonrasında), tanımladıkları ifadelerin görünür yürütme planında göründüğü konumda mutlaka değerlendirilmesi gerekmez ; Hesaplamanın sonucu daha sonraki bir operatör tarafından istenene kadar değerlendirme ertelenebilir.

Bu sorguda, target_datastring tipik olarak büyüktür ve string'den XMLpahalıya dönüşüm yapar . Yavaş planlarda, XMLdönüşüm dizesi sonucu gerektiren bir sonraki işleç her seferinde Expr1000geri tepme gerçekleştirilir.

Yeniden bağlama, iç içe geçmiş ilmeklerin iç tarafında, ilişkili bir parametre (dış referans) değiştiğinde birleşir. Expr1000iç içe geçmiş halkaların çoğu için bu uygulama planına katılan bir dış referanstır. İfade birden fazla XML Okuyucusu, hem Stream Aggregates, hem de bir başlangıç ​​Filtresi tarafından defalarca belirtilir. Boyutuna bağlı olarak, XMLdizginin dönüştürüldüğü XMLsayı milyonlarca kolayca sayı yapabilir.

Aşağıdaki çağrı yığınları target_data, dönüştürülecek dizgenin örneklerini göstermektedir XML( ConvertStringToXMLForES- ES, İfade Hizmetidir ):

Başlangıç ​​Filtresi

Başlangıç ​​Filtresi çağrı yığını

XML Okuyucu (dahili olarak TVF Stream)

TVF Akışı çağrı yığını

Akış Toplulaştırması

Akış Toplu arama yığını

Dize dönüştürme XML bu operatörlerin yeniden bağladığı her seferinde iç içe geçmiş döngü planları ile gözlenen performans farkını açıklar. Bu, paralelliğin kullanılıp kullanılmamasından bağımsızdır. Bu sadece, MAXDOP 1ipucu belirtildiğinde optimizer bir karma birleştirmeyi seçtiğinde gerçekleşir . Eğer MAXDOP 1, LOOP JOINbelirtilirse, performans sadece (iyileştirici iç içe döngüler seçer) varsayılan paralel planı ile olarak zayıftır.

Bir karma birleştirmede performansın ne kadar artacağı Expr1000, operatörün yapısında veya sonda tarafında görünmesine bağlıdır . Aşağıdaki sorgu, prob tarafındaki ifadeyi bulur:

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_sessions s
    INNER HASH JOIN sys.dm_xe_session_targets st 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';

Birleştirme işlemlerinin yazılı sırasını soruda gösterilen sürümden tersine çevirdim, çünkü birleştirme ipuçları ( INNER HASH JOINyukarıdaki) de FORCE ORDERbelirtildiği gibi tüm sorgunun sırasını zorlar . Expr1000Prob tarafında görünmesini sağlamak için tersine çevirme gereklidir . İcra planının ilginç kısmı:

ipucu 1

Prob tarafında tanımlanan ifade ile, değer önbelleğe alınır:

Önbelleği Sakla

Expr1000İlk operatör değere (yukarıdaki yığın izinde başlangıç ​​filtresi) ihtiyaç duyuncaya kadar değerlendirme hala ertelenir, ancak hesaplanan değer önbelleğe alınır ( CValHashCachedSwitch) ve daha sonra XML Okuyucular ve Akım Toplamaları tarafından yapılan çağrılar için tekrar kullanılır. Aşağıdaki yığın izi, bir XML Reader tarafından yeniden kullanılan önbellek değerinin bir örneğini gösterir.

Önbellek yeniden kullanımı

Birleştirme sırası, tanımlamanın Expr1000karma birleştirmenin yapı tarafında gerçekleşeceği şekilde zorlandığında durum farklıdır:

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 
    INNER HASH 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'

Hash 2

Bir karma birleştirme, eşleşmeler için tarama başlamadan önce bir karma tablosu oluşturmak için derleme girişini tamamen okur. Sonuç olarak, sadece planın prob tarafından çalışılan iş parçacığı başına değil, tüm değerleri depolamamız gerekir . Bu nedenle karma birleştirme verileri tempdbdepolamak için bir çalışma masası kullanır XMLve Expr1000daha sonraki operatörler tarafından yapılan sonuçlara her erişim için pahalı bir yolculuk gerekir tempdb:

Yavaş erişim

Aşağıda yavaş erişim yolunun daha fazla ayrıntı gösterilmektedir:

Yavaş ayrıntılar

Bir birleştirme birleşimine zorlanırsa, giriş satırları sıralanır (bir karma birleştirme için derleme girişi gibi bir engelleme işlemi gibi). tempdb , verilerin boyutu nedeniyle sıralama için optimize edilmiş çalışma tablası .

Büyük veri öğelerini manipüle eden planlar, yürütme planında görünmeyen her türlü nedenden dolayı sorunlu olabilir. Bir karma birleşimini kullanmak (doğru girdideki ifade ile) iyi bir çözüm değildir. Belgelenmemiş iç davranışa dayanarak gelecek hafta aynı şekilde çalışacağını veya biraz farklı bir sorguda çalışacağını garanti etmeden güveniyor.

Mesaj XMLmanipülasyon bugün optimize etmek zor şeyler olabilir. XMLParçalamadan önce değişken veya geçici bir tabloya yazma, yukarıda gösterilenlerden çok daha sağlam bir geçici çözümdür. Bunu yapmanın bir yolu:

DECLARE @data xml =
        CONVERT
        (
            xml,
            (
            SELECT TOP (1)
                dxst.target_data
            FROM sys.dm_xe_sessions AS dxs 
            JOIN sys.dm_xe_session_targets AS dxst ON
                dxst.event_session_address = dxs.[address]
            WHERE 
                dxs.name = N'system_health'
                AND dxst.target_name = N'ring_buffer'
            )
        )

SELECT XEventData.XEvent.value('(data/value)[1]', 'varchar(max)')
FROM @data.nodes ('./RingBufferTarget/event[@name eq "xml_deadlock_report"]') AS XEventData (XEvent)
WHERE XEventData.XEvent.value('@name', 'varchar(4000)') = 'xml_deadlock_report';

Son olarak, Martin'in çok güzel bir grafiğini aşağıdaki yorumlardan eklemek istiyorum:

Martin'nin grafiği


Harika bir açıklama, teşekkür ederim. Hesaplama skalerleriyle ilgili yazınızı da okudum, ancak burada iki ve ikiyi bir araya getirmedim.
Martin Smith

3
Dün profil oluşturma denemesiyle ilgili bir şeyi karıştırmış olmalıyım (belki yavaş ve hızlı izler bulaştırılmış!). Bugün yeniden yaptım ve tabii ki ne söylediğini gösteriyor.
Martin Smith,

2
Evet, ekran görüntüsü Visual Studio 2012 profilcisinden Ağaç Görünümü Çağrısı raporu . Görünüşe göre gizemli ipler olmasa da yöntem adlarının çıktınızda çok daha net @@IEAAXPEA_Kgöründüğünü düşünüyorum.
Martin Smith

10

Aslen buraya gönderilen makalemin kodu:

http://www.sqlservercentral.com/articles/deadlock/65658/

Yorumları okuduysanız, karşılaştığınız performans sorunlarına sahip olmayan, biri orijinal sorgunun bir modifikasyonunu kullanan, diğeri ise işlemden önce XML'i tutmak için bir değişkeni kullanan birkaç alternatif bulacaksınız. daha iyi. (Sayfa 2'deki yorumlarıma bakın) DMV'lerden gelen XML'in işlenmesi yavaş olabilir, çünkü DMF'den XML'i ayrıştırma işlemi, genellikle verileri geçici bir tabloya alıp daha sonra işleyerek daha iyi sonuçlanan dosya hedefi için DMF'den ayrıştırma işlemi yapabilir. SQL'deki XML, .NET veya SQLCLR gibi şeyleri kullanmaya kıyasla yavaş.


1
Teşekkürler! Hile yaptı. Değişken alma 600ms ve 6341 olmadan bir okur ve değişken ile 303 msve 3249 lob reads. 2012'de and target_name='ring_buffer', şu anda iki hedef varmış gibi göründüğü için o sürüme eklemem gerekiyordu. Yine de 20 dakikalık versiyonda tam olarak ne yaptığına dair zihinsel bir imaj elde etmeye çalışıyorum.
Martin Smith
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.