Hiệu suất rất lạ với chỉ mục XML


32

Câu hỏi của tôi dựa trên điều này: https://stackoverflow.com/q/35575990/5089204

Để đưa ra câu trả lời, tôi đã thực hiện kịch bản kiểm tra sau.

Kịch bản thử nghiệm

Đầu tiên tôi tạo một bảng thử nghiệm và điền vào nó với 100.000 hàng. Một số ngẫu nhiên (0 đến 1000) sẽ dẫn đến ~ 100 hàng cho mỗi số ngẫu nhiên. Số này được đặt vào một cột varchar và như một giá trị trong XML của bạn.

Sau đó, tôi thực hiện một cuộc gọi như OP ở đó cần nó với .exist () và với .nodes () với một lợi thế nhỏ cho lần thứ hai, nhưng cả hai đều mất từ ​​5 đến 6 giây. Trong thực tế, tôi thực hiện các cuộc gọi hai lần: lần thứ hai theo thứ tự hoán đổi và với các thông số tìm kiếm thay đổi một chút và với "// mục" thay vì đường dẫn đầy đủ để tránh kết quả dương tính thông qua kết quả hoặc kế hoạch được lưu trong bộ nhớ cache.

Sau đó, tôi tạo một chỉ mục XML và thực hiện các cuộc gọi tương tự

Bây giờ - điều thực sự làm tôi ngạc nhiên! - sự .nodesvới đường dẫn đầy đủ là chậm hơn nhiều so với trước (9 giây) nhưng .exist()giảm xuống còn một nửa thứ hai, với đường dẫn đầy đủ thậm chí xuống đến khoảng 0.10 giây. (trong khi .nodes()với con đường ngắn là tốt hơn, nhưng vẫn còn xa phía sau .exist())

Câu hỏi:

Các thử nghiệm của riêng tôi đưa ra ngắn gọn: các chỉ mục XML có thể làm nổ tung cơ sở dữ liệu cực kỳ. Họ có thể tăng tốc mọi thứ cực kỳ (s. Chỉnh sửa 2), nhưng cũng có thể làm chậm các truy vấn của bạn. Tôi muốn hiểu cách chúng hoạt động ... Khi nào nên tạo một chỉ mục XML? Tại sao .nodes()với một chỉ số có thể tồi tệ hơn mà không có? Làm thế nào người ta có thể tránh được tác động tiêu cực?

CREATE TABLE #testTbl(ID INT IDENTITY PRIMARY KEY, SomeData VARCHAR(100),XmlColumn XML);
GO

DECLARE @RndNumber VARCHAR(100)=(SELECT CAST(CAST(RAND()*1000 AS INT) AS VARCHAR(100)));

INSERT INTO #testTbl VALUES('Data_' + @RndNumber,
'<error application="application" host="host" type="exception" message="message" >
  <serverVariables>
    <item name="name1">
      <value string="text" />
    </item>
    <item name="name2">
      <value string="text2" />
    </item>
    <item name="name3">
      <value string="text3" />
    </item>
    <item name="name4">
      <value string="text4" />
    </item>
    <item name="name5">
      <value string="My test ' +  @RndNumber + '" />
    </item>
    <item name="name6">
      <value string="text6" />
    </item>
    <item name="name7">
      <value string="text7" />
    </item>
  </serverVariables>
</error>');

GO 100000

DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesFullPath_no_index;
GO

DECLARE @d DATETIME=GETDATE();
SELECT * 
FROM #testTbl
WHERE XmlColumn.exist('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistFullPath_no_index;
GO

DECLARE @d DATETIME=GETDATE();
SELECT * 
FROM #testTbl
WHERE XmlColumn.exist('//item[@name="name5" and value/@string="My test 500"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistShortPath_no_index;
GO

DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('//item[@name="name5" and value/@string="My test 500"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesShortPath_no_index;
GO

CREATE PRIMARY XML INDEX PXML_test_XmlColum1 ON #testTbl(XmlColumn);
CREATE XML INDEX IXML_test_XmlColumn2 ON #testTbl(XmlColumn) USING XML INDEX PXML_test_XmlColum1 FOR PATH;
GO

DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesFullPath_with_index;
GO

DECLARE @d DATETIME=GETDATE();
SELECT * 
FROM #testTbl
WHERE XmlColumn.exist('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistFullPath_with_index;
GO

DECLARE @d DATETIME=GETDATE();
SELECT * 
FROM #testTbl
WHERE XmlColumn.exist('//item[@name="name5" and value/@string="My test 500"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistShortPath_with_index;
GO

DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('//item[@name="name5" and value/@string="My test 500"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesShortPath_with_index;
GO

DROP TABLE #testTbl;

EDIT 1 - Kết quả

Đây là một kết quả với SQL Server 2012 được cài đặt cục bộ trên máy tính xách tay trung bình Trong thử nghiệm này, tôi không thể tái tạo tác động tiêu cực cực đoan trên NodesFullPath_with_index, mặc dù nó chậm hơn so với không có chỉ mục ...

NodesFullPath_no_index    6.067
ExistFullPath_no_index    6.223
ExistShortPath_no_index   8.373
NodesShortPath_no_index   6.733

NodesFullPath_with_index  7.247
ExistFullPath_with_index  0.217
ExistShortPath_with_index 0.500
NodesShortPath_with_index 2.410

Kiểm tra EDIT 2 với XML lớn hơn

Theo đề xuất của TT, tôi đã sử dụng XML ở trên, nhưng đã sao chép các itemmã để đạt khoảng 450 mục. Tôi để nút hit rất cao trong XML (vì tôi nghĩ rằng nó .exist()sẽ dừng ở lần truy cập đầu tiên, trong khi .nodes()sẽ tiếp tục)

Tạo chỉ mục XML đã thổi tệp mdf lên ~ 21GB, ~ 18GB dường như thuộc về chỉ mục (!!!)

NodesFullPath_no_index    3min44
ExistFullPath_no_index    3min39
ExistShortPath_no_index   3min49
NodesShortPath_no_index   4min00

NodesFullPath_with_index  8min20
ExistFullPath_with_index  8,5 seconds !!!
ExistShortPath_with_index 1min21
NodesShortPath_with_index 13min41 !!!

Câu trả lời:


33

Chắc chắn có rất nhiều thứ đang diễn ra ở đây vì vậy chúng ta sẽ phải xem điều này dẫn đến đâu.

Trước hết, sự khác biệt về thời gian giữa SQL Server 2012 và SQL Server 2014 là do công cụ ước tính cardinality mới trong SQL Server 2014. Bạn có thể sử dụng cờ theo dõi trong SQL Server 2014 để buộc công cụ ước tính cũ và sau đó bạn sẽ thấy cùng thời gian các đặc điểm trong SQL Server 2014 như trong SQL Server 2012.

So sánh nodes()so với exist()không công bằng vì chúng sẽ không trả về cùng một kết quả nếu có nhiều hơn một phần tử được so khớp trong XML cho một hàng. exist()sẽ trả về một hàng từ bảng cơ sở bất kể, trong khi đó nodes()có khả năng cung cấp cho bạn nhiều hơn một hàng được trả về cho mỗi hàng trong bảng cơ sở.
Chúng tôi biết dữ liệu nhưng SQL Server không và phải xây dựng một kế hoạch truy vấn để xem xét điều đó.

Để làm cho nodes()truy vấn tương đương với exist()truy vấn, bạn có thể làm một cái gì đó như thế này.

SELECT testTbl.*
FROM testTbl
WHERE EXISTS (
             SELECT *
             FROM XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b)
             )

Với một truy vấn như vậy, không có sự khác biệt giữa việc sử dụng nodes()hoặc exist()đó là do SQL Server xây dựng gần như cùng một kế hoạch cho hai phiên bản không sử dụng một chỉ mục và chính xác cùng một kế hoạch khi sử dụng chỉ mục. Điều đó đúng cho cả SQL Server 2012 và SQL Server 2014.

Đối với tôi trong SQL Server 2012, các truy vấn không có chỉ mục XML mất 6 giây bằng cách sử dụng phiên bản sửa đổi của nodes()truy vấn ở trên. Không có sự khác biệt giữa việc sử dụng đường dẫn đầy đủ hoặc đường dẫn ngắn. Với chỉ mục XML, phiên bản đường dẫn đầy đủ là nhanh nhất và mất 5 ms và sử dụng đường dẫn ngắn mất khoảng 500 ms. Kiểm tra các kế hoạch truy vấn sẽ cho bạn biết tại sao có sự khác biệt nhưng phiên bản ngắn là khi bạn sử dụng một đường dẫn ngắn, SQL Server tìm kiếm trong chỉ mục trên đường dẫn ngắn (một phạm vi tìm kiếm sử dụng like) và trả về 700000 hàng trước khi loại bỏ các hàng đó không khớp với giá trị Khi sử dụng đường dẫn đầy đủ, SQL Server có thể sử dụng biểu thức đường dẫn trực tiếp cùng với giá trị của nút để thực hiện tìm kiếm và chỉ trả về 105 hàng từ đầu để làm việc.

Sử dụng SQL Server 2014 và công cụ ước tính cardinalty mới, không có sự khác biệt trong các truy vấn này khi sử dụng chỉ mục XML. Không sử dụng chỉ mục, các truy vấn vẫn mất cùng thời gian nhưng chỉ mất 15 giây. Rõ ràng không phải là một cải tiến ở đây khi sử dụng công cụ mới.

Không chắc chắn nếu tôi hoàn toàn mất dấu về câu hỏi của bạn thực sự là gì vì tôi đã sửa đổi các truy vấn thành tương đương nhưng đây là những gì tôi tin rằng bây giờ.

Tại sao nodes()truy vấn (phiên bản gốc) có chỉ mục XML chậm hơn đáng kể khi chỉ mục không được sử dụng?

Vâng, câu trả lời là trình tối ưu hóa kế hoạch truy vấn SQL Server làm điều gì đó xấu và đó là giới thiệu một toán tử bộ đệm. Tôi không biết tại sao nhưng tin tốt là nó không còn ở đó với công cụ ước tính cardinalty mới trong SQL Server 2014.
Không có chỉ mục nào, truy vấn mất khoảng 7 giây cho dù sử dụng công cụ ước tính cardinality nào. Với chỉ mục, phải mất 15 giây với công cụ ước tính cũ (SQL Server 2012) và khoảng 2 giây với công cụ ước tính mới (SQL Server 2014).

Lưu ý: Những phát hiện ở trên là hợp lệ với dữ liệu thử nghiệm của bạn. Có thể có một câu chuyện hoàn toàn khác để nói nếu bạn thay đổi kích thước, hình dạng hoặc hình thức của XML. Không có cách nào để biết chắc chắn mà không kiểm tra dữ liệu bạn thực sự có trong các bảng.

Các chỉ mục XML hoạt động như thế nào

Các chỉ mục XML trong SQL Server được triển khai như các bảng bên trong. Chỉ mục XML chính tạo bảng với khóa chính của bảng cơ sở cộng với cột id nút, trong tổng số 12 cột. Nó sẽ có một hàng trên mỗi element/node/attribute etc.bảng để tất nhiên bảng có thể trở nên thực sự lớn tùy thuộc vào kích thước của XML được lưu trữ. Với một chỉ mục XML chính tại chỗ, SQL Server có thể sử dụng khóa chính của bảng bên trong để định vị các nút và giá trị XML cho mỗi hàng trong bảng cơ sở.

Các chỉ mục XML thứ cấp có ba loại. Khi bạn tạo một chỉ mục XML thứ cấp, có một chỉ mục không được nhóm được tạo trên bảng bên trong và tùy thuộc vào loại chỉ mục phụ bạn tạo, nó sẽ có các cột và thứ tự cột khác nhau.

Từ CREATE XML INDEX (Transact-SQL) :

VALUE
Tạo một chỉ mục XML thứ cấp trên các cột trong đó các cột chính là (giá trị nút và đường dẫn) của chỉ mục XML chính.

PATH
Tạo một chỉ mục XML thứ cấp trên các cột được xây dựng trên các giá trị đường dẫn và giá trị nút trong chỉ mục XML chính. Trong chỉ mục phụ PATH, các giá trị đường dẫn và nút là các cột chính cho phép tìm kiếm hiệu quả khi tìm kiếm đường dẫn.

SỞ HỮU
Tạo một chỉ mục XML thứ cấp trên các cột (PK, đường dẫn và giá trị nút) của chỉ mục XML chính trong đó PK là khóa chính của bảng cơ sở.

Vì vậy, khi bạn tạo một chỉ mục PATH, cột đầu tiên trong chỉ mục đó là biểu thức đường dẫn và cột thứ hai là giá trị trong nút đó. Trên thực tế, đường dẫn được lưu trữ trong một loại định dạng nén và đảo ngược. Rằng nó được lưu trữ đảo ngược là những gì làm cho nó hữu ích trong các tìm kiếm bằng cách sử dụng các biểu thức đường dẫn ngắn. Trong trường hợp đường dẫn ngắn của bạn, bạn đã tìm kiếm //item/value/@string, //item/@name//item. Vì đường dẫn được lưu trữ đảo ngược trong cột, SQL Server có thể sử dụng phạm vi tìm kiếm like = '€€€€€€%trong đó €€€€€€đường dẫn được đảo ngược. Khi bạn sử dụng một đường dẫn đầy đủ, không có lý do để sử dụng likevì toàn bộ đường dẫn được mã hóa trong cột và giá trị cũng có thể được sử dụng trong vị từ tìm kiếm.

Câu hỏi của bạn :

Khi nào nên tạo một chỉ mục XML?

Như một phương sách cuối cùng nếu có. Tốt hơn là thiết kế cơ sở dữ liệu của bạn để bạn không phải sử dụng các giá trị bên trong XML để lọc theo mệnh đề where. Nếu bạn biết trước rằng bạn cần phải làm điều đó, bạn có thể sử dụng quảng cáo tài sản để tạo một cột được tính toán mà bạn có thể lập chỉ mục nếu cần. Kể từ SQL Server 2012 SP1, bạn cũng có sẵn các chỉ mục XML chọn lọc. Các hoạt động đằng sau cảnh này khá giống với các chỉ mục XML thông thường, chỉ có bạn chỉ định biểu thức đường dẫn trong định nghĩa chỉ mục và chỉ các nút phù hợp được lập chỉ mục. Bằng cách đó bạn có thể tiết kiệm rất nhiều không gian.

Tại sao .nodes () với chỉ mục có thể tệ hơn không có?

Khi có một chỉ mục XML được tạo trên một bảng, SQL Server sẽ luôn sử dụng chỉ mục đó (các bảng bên trong) để lấy dữ liệu. Quyết định đó được thực hiện trước khi trình tối ưu hóa có tiếng nói về điều gì nhanh và điều gì không nhanh. Đầu vào của trình tối ưu hóa được viết lại để nó đang sử dụng các bảng bên trong và sau đó, tùy thuộc vào trình tối ưu hóa để làm tốt nhất như với một truy vấn thông thường. Khi không có chỉ mục nào được sử dụng, có một vài hàm có giá trị bảng được sử dụng thay thế. Điểm mấu chốt là bạn không thể biết cái gì sẽ nhanh hơn mà không cần kiểm tra.

Làm thế nào người ta có thể tránh được tác động tiêu cực?

Kiểm tra


2
Ý tưởng của bạn về sự khác biệt .nodes().exist()có sức thuyết phục. Ngoài ra thực tế là chỉ số với full path searchnhanh hơn có vẻ dễ hiểu. Điều này có nghĩa: Nếu bạn tạo một chỉ mục XML, bạn phải luôn luôn nhận thức được những ảnh hưởng tiêu cực với bất kỳ XPath generic ( //hoặc *hoặc ..hoặc [filter]hoặc bất cứ điều gì không chỉ đơn giản XPath ...). Trong thực tế, bạn chỉ nên sử dụng đường dẫn đầy đủ - một kết quả rút lui tuyệt vời ...
Shnugo
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.