Tối ưu hóa các kế hoạch với các trình đọc XML


34

Thực hiện truy vấn từ đây để kéo các sự kiện bế tắc ra khỏi phiên sự kiện mở rộng mặc định

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

mất khoảng 20 phút để hoàn thành trên máy của tôi. Số liệu thống kê được báo cáo là

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.

Kế hoạch chậm XML

Song song, tương đông

Nếu tôi loại bỏ WHEREmệnh đề, nó sẽ hoàn thành trong chưa đầy một giây trả về 3.782 hàng.

Tương tự như vậy nếu tôi thêm OPTION (MAXDOP 1)vào truy vấn ban đầu, nó cũng tăng tốc mọi thứ với các số liệu thống kê hiện hiển thị số lần đọc lob ít hơn.

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.

Kế hoạch XML nhanh hơn

Nối tiếp

Vì vậy, câu hỏi của tôi là

Bất cứ ai có thể giải thích những gì đang xảy ra? Tại sao kế hoạch ban đầu lại tồi tệ đến mức thảm khốc và có cách nào đáng tin cậy để tránh vấn đề này không?

Thêm vào:

Tôi cũng thấy rằng việc thay đổi truy vấn để INNER HASH JOINcải thiện mọi thứ ở một mức độ nào đó (nhưng vẫn mất> 3 phút) vì kết quả DMV rất nhỏ. Tôi nghi ngờ rằng chính loại Tham gia chịu trách nhiệm và cho rằng một thứ khác phải thay đổi. Số liệu thống kê cho điều đó

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.

(Và kế hoạch)

Sau khi điền vào bộ đệm vòng sự kiện mở rộng ( DATALENGTHtrong số XML4,880,045 byte và nó chứa 1,448 sự kiện.) Và thử nghiệm phiên bản cắt giảm của truy vấn ban đầu có và không có MAXDOPgợi ý.

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 

Đã cho kết quả như sau

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

Có một sự khác biệt rõ ràng trong phân bổ tempdb với các 616trang hiển thị nhanh hơn được phân bổ và phân bổ. Đây là cùng số lượng trang được sử dụng khi XML cũng được đặt vào một biến.

Đối với kế hoạch chậm, số lượng phân bổ trang này là hàng triệu. Bỏ phiếu dm_db_task_space_usagetrong khi truy vấn đang chạy cho thấy dường như liên tục phân bổ và phân bổ các trang ở tempdbbất kỳ nơi nào từ 1.800 đến 3.000 trang được phân bổ cùng một lúc.


Bạn có thể di chuyển WHEREmệnh đề vào biểu thức XQuery; logic không phải được loại bỏ để nó đi nhanh : TargetData.nodes ('RingBufferTarget[1]/event[@name = "xml_deadlock_report"]'). Điều đó nói rằng, tôi không biết nội bộ XML đủ tốt để trả lời câu hỏi bạn đã đặt ra.
Jon Seigel

Paging @QueryPoolBoy cho bạn Martin ... anh ấy đề nghị xem qua các bình luận ở đây nơi anh ấy có các đề xuất hiệu quả hơn (chúng dựa trên bài viết nguồn cho mã ở trên ).
Aaron Bertrand

Câu trả lời:


36

Lý do cho sự khác biệt hiệu năng nằm ở cách xử lý các biểu thức vô hướng trong công cụ thực thi. Trong trường hợp này, biểu thức quan tâm là:

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

Nhãn biểu thức này được xác định bởi toán tử vô hướng tính toán (nút 11 trong kế hoạch nối tiếp, nút 13 trong kế hoạch song song). Tính toán toán tử vô hướng khác với các toán tử khác (SQL Server 2005 trở đi) ở chỗ (các) biểu thức mà chúng xác định không nhất thiết phải được đánh giá tại vị trí chúng xuất hiện trong kế hoạch thực hiện có thể nhìn thấy; đánh giá có thể được hoãn lại cho đến khi kết quả tính toán được yêu cầu bởi một nhà điều hành sau này.

Trong truy vấn hiện tại, target_datachuỗi thường lớn, làm cho việc chuyển đổi từ chuỗi thành XMLđắt tiền. Trong các kế hoạch chậm, chuỗi XMLchuyển đổi được thực hiện mỗi khi toán tử sau yêu cầu kết quả Expr1000là bật lại.

Rebinding xảy ra ở phía bên trong của một vòng lặp lồng nhau tham gia khi một tham số tương quan (tham chiếu bên ngoài) thay đổi. Expr1000là một tham chiếu bên ngoài cho hầu hết các vòng lặp lồng nhau tham gia trong kế hoạch thực hiện này. Biểu thức được tham chiếu nhiều lần bởi một số Trình đọc XML, cả Tập hợp luồng và bởi Bộ lọc khởi động. Tùy thuộc vào kích thước của XML, số lần chuỗi được chuyển đổi XMLcó thể dễ dàng đánh số trong hàng triệu.

Các ngăn xếp cuộc gọi bên dưới hiển thị các ví dụ về target_datachuỗi được chuyển đổi thành XML( ConvertStringToXMLForES- trong đó ES là Dịch vụ Biểu thức ):

Bộ lọc khởi động

Bộ lọc cuộc gọi khởi động

Trình đọc XML (Truyền TVF nội bộ)

Ngăn xếp cuộc gọi TVF Stream

Tổng hợp luồng

Luồng cuộc gọi tổng hợp

Chuyển đổi chuỗi thành XMLmỗi lần bất kỳ toán tử nào khởi động lại giải thích sự khác biệt hiệu năng quan sát được với các kế hoạch vòng lặp lồng nhau. Điều này là bất kể sử dụng song song hay không. Nó chỉ xảy ra khi trình tối ưu hóa chọn tham gia băm khi MAXDOP 1gợi ý được chỉ định. Nếu MAXDOP 1, LOOP JOINđược chỉ định, hiệu suất sẽ kém như với gói song song mặc định (trong đó trình tối ưu hóa chọn các vòng lặp lồng nhau).

Hiệu suất tăng bao nhiêu với phép nối băm phụ thuộc vào việc Expr1000xuất hiện ở phía xây dựng hay đầu dò của toán tử. Truy vấn sau đây định vị biểu thức ở phía đầu dò:

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

Tôi đã đảo ngược thứ tự bằng văn bản của các phép nối từ phiên bản được hiển thị trong câu hỏi, bởi vì gợi ý nối ( INNER HASH JOINở trên) cũng buộc thứ tự cho toàn bộ truy vấn, giống như FORCE ORDERđã được chỉ định. Sự đảo ngược là cần thiết để đảm bảo Expr1000xuất hiện ở phía đầu dò. Phần thú vị của kế hoạch thực hiện là:

gợi ý 1

Với biểu thức được xác định ở phía đầu dò, giá trị được lưu trữ:

Bộ nhớ cache băm

Việc đánh giá Expr1000vẫn được hoãn lại cho đến khi toán tử đầu tiên cần giá trị (bộ lọc khởi động trong theo dõi ngăn xếp ở trên) nhưng giá trị được tính toán được lưu vào bộ đệm ( CValHashCachedSwitch) và được sử dụng lại cho các cuộc gọi sau của Trình đọc XML và Tập hợp luồng. Theo dõi ngăn xếp dưới đây cho thấy một ví dụ về giá trị được lưu trong bộ nhớ cache đang được Trình đọc XML sử dụng lại.

Tái sử dụng bộ nhớ cache

Khi thứ tự tham gia bị buộc sao cho định nghĩa Expr1000xảy ra ở phía xây dựng của phép nối băm, tình huống sẽ khác:

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'

Băm 2

Một phép nối băm đọc hoàn toàn đầu vào xây dựng của nó để xây dựng bảng băm trước khi bắt đầu thăm dò các kết quả khớp. Kết quả là, chúng ta phải lưu trữ tất cả các giá trị, không chỉ một giá trị cho mỗi luồng được xử lý từ phía đầu dò của kế hoạch. Do đó, phép nối băm sử dụng một tempdbbảng làm việc để lưu trữ XMLdữ liệu và mọi truy cập vào kết quả của các Expr1000nhà khai thác sau này đòi hỏi một chuyến đi đắt tiền để tempdb:

Truy cập chậm

Sau đây cho thấy nhiều chi tiết hơn về đường dẫn truy cập chậm:

Chi tiết chậm

Nếu một phép nối hợp nhất bị buộc các hàng đầu vào được sắp xếp (một thao tác chặn, giống như đầu vào xây dựng cho phép nối băm) dẫn đến một sự sắp xếp tương tự trong đó tempdbyêu cầu truy cập chậm thông qua một bàn làm việc được tối ưu hóa sắp xếp vì kích thước của dữ liệu.

Các kế hoạch thao túng các mục dữ liệu lớn có thể có vấn đề vì tất cả các loại lý do không rõ ràng từ kế hoạch thực hiện. Sử dụng phép nối băm (với biểu thức trên đầu vào chính xác) không phải là một giải pháp tốt. Nó dựa vào hành vi nội bộ không có giấy tờ mà không đảm bảo nó sẽ hoạt động theo cách tương tự vào tuần tới, hoặc trên một truy vấn hơi khác.

Thông điệp là XMLthao tác có thể là những điều khó khăn để tối ưu hóa ngày nay. Viết XMLvào một bảng biến hoặc tạm thời trước khi băm là một cách giải quyết vững chắc hơn nhiều so với bất cứ điều gì được hiển thị ở trên. Một cách để làm điều này là:

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

Cuối cùng, tôi chỉ muốn thêm đồ họa rất đẹp của Martin từ các bình luận bên dưới:

Đồ họa của Martin


Giải thích tuyệt vời, cảm ơn bạn. Tôi cũng đã đọc bài viết của bạn về tính toán vô hướng nhưng không đặt hai và hai lại với nhau ở đây.
Martin Smith

3
Tôi đã phải làm rối tung một cái gì đó với nỗ lực của tôi trong hồ sơ ngày hôm qua (có thể nhầm lẫn dấu vết chậm và nhanh!). Tôi đã làm lại nó ngày hôm nay và tất nhiên nó chỉ cho thấy những gì bạn đã nói.
Martin Smith

2
Có, ảnh chụp màn hình là báo cáo Call Tree View từ trình tạo hồ sơ Visual Studio 2012 . Tôi nghĩ rằng các tên phương thức trông rõ ràng hơn nhiều trong đầu ra của bạn mặc dù không có các chuỗi bí ẩn như @@IEAAXPEA_Kxuất hiện.
Martin Smith

10

Đó là mã từ bài viết của tôi ban đầu được đăng ở đây:

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

Nếu bạn đọc các bình luận, bạn sẽ tìm thấy một vài lựa chọn thay thế không có vấn đề về hiệu năng mà bạn đang gặp phải, một là sử dụng một sửa đổi của truy vấn ban đầu đó và một sử dụng một biến để giữ XML trước khi xử lý nó tốt hơn. (xem nhận xét của tôi về Trang 2) XML từ DMV có thể xử lý chậm, vì có thể phân tích cú pháp XML từ DMF cho mục tiêu tệp thường được hoàn thành tốt hơn bằng cách đọc dữ liệu vào bảng tạm thời trước rồi xử lý. XML trong SQL chậm khi so sánh với việc sử dụng những thứ như .NET hoặc SQLCLR.


1
Cảm ơn! Điều đó đã lừa Một cái không có biến mất 600ms và 6341 đọc và với biến 303 ms3249 lob reads. Vào năm 2012 tôi cũng cần thêm and target_name='ring_buffer'vào phiên bản đó vì dường như có hai mục tiêu bây giờ. Tôi vẫn đang cố gắng để có được một hình ảnh tinh thần về chính xác những gì nó đang làm trong phiên bản 20 phút.
Martin Smith
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.