ประสิทธิภาพที่แปลกประหลาดมากกับดัชนี XML


32

คำถามของฉันเป็นไปตามนี้: https://stackoverflow.com/q/35575990/5089204

เพื่อให้คำตอบนั้นฉันได้ทำการทดสอบสถานการณ์ต่อไปนี้

สถานการณ์การทดสอบ

ก่อนอื่นฉันจะสร้างตารางทดสอบและเติมเต็ม 100.000 แถว ตัวเลขสุ่ม (0 ถึง 1,000) ควรนำไปสู่ ​​~ 100 แถวสำหรับแต่ละหมายเลขสุ่ม หมายเลขนี้ถูกใส่ในคอลัมน์ varchar และเป็นค่าใน XML ของคุณ

จากนั้นฉันจะโทรเช่น OP มีความต้องการด้วย. exist () และ. nodes () โดยมีข้อได้เปรียบเล็กน้อยสำหรับวินาที แต่ทั้งคู่ใช้เวลา 5 ถึง 6 วินาที ในความเป็นจริงฉันทำการโทรสองครั้ง: ครั้งที่สองในลำดับที่สลับกันและเปลี่ยนการค้นหาเล็กน้อยและใช้ "// item" แทนพา ธ เต็มเพื่อหลีกเลี่ยงผลบวกปลอมผ่านผลลัพธ์หรือแผนแคช

จากนั้นฉันจะสร้างดัชนี XML และทำการโทรเดียวกัน

ตอนนี้ - อะไรทำให้ฉันประหลาดใจจริงๆ! - the .nodeswith full pathนั้นช้ากว่ามาก (9 วินาที) แต่.exist()ลงไปครึ่งวินาทีโดยที่full pathจะลดลงเหลือประมาณ 0.10 วินาที (ในขณะที่.nodes()มีเส้นทางที่สั้นจะดีกว่า แต่ยังห่างไกลอยู่เบื้องหลัง.exist())

คำถาม:

การทดสอบสั้น ๆ ของฉันเอง: ดัชนี XML สามารถระเบิดฐานข้อมูลได้อย่างมาก พวกเขาสามารถเพิ่มความเร็วในสิ่งต่าง ๆ ได้อย่างมาก (แก้ไข 2) แต่อาจทำให้ข้อความค้นหาของคุณช้าลงเช่นกัน ฉันต้องการที่จะเข้าใจว่ามันทำงานอย่างไร ... เมื่อไหร่ที่ควรจะสร้างดัชนี XML? ทำไมถึง.nodes()มีดัชนีจะเลวร้ายยิ่งกว่าไม่มี? เราจะหลีกเลี่ยงผลกระทบของ negativ ได้อย่างไร?

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;

แก้ไข 1 - ผลลัพธ์

นี่เป็นผลอย่างหนึ่งกับ SQL Server 2012 ที่ติดตั้งไว้ในเครื่องแล็ปท็อปขนาดกลางในการทดสอบนี้ฉันไม่สามารถสร้างผลกระทบ negativ ได้อย่างNodesFullPath_with_indexมากถึงแม้ว่ามันจะช้ากว่าที่ไม่มีดัชนี ...

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

แก้ไข 2 ทดสอบด้วย XML ที่ใหญ่กว่า

ตามคำแนะนำของ TT ฉันใช้ XML ด้านบน แต่คัดลอกitem-node เพื่อเข้าถึงรายการประมาณ 450 รายการ ฉันปล่อยให้ hit-node สูงมากใน XML (เพราะฉันคิดว่า.exist()มันจะหยุดในการเข้าชมครั้งแรกในขณะที่.nodes()จะดำเนินการต่อ)

การสร้างดัชนี XML ทำให้ไฟล์ mdf เป็น ~ 21GB ดูเหมือนว่า ~ 18GB จะเป็นของดัชนี (!!!)

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

คำตอบ:


33

แน่นอนว่ามีหลายอย่างเกิดขึ้นที่นี่ดังนั้นเราจะต้องดูว่าสิ่งนี้นำไปสู่อะไร

ก่อนอื่นความแตกต่างของเวลาระหว่าง SQL Server 2012 และ SQL Server 2014 นั้นเกิดจากตัวประมาณค่า cardinality ใหม่ใน SQL Server 2014 คุณสามารถใช้ค่าสถานะการติดตามใน SQL Server 2014 เพื่อบังคับตัวประมาณแบบเก่าแล้วคุณจะเห็นเวลาเดียวกัน ลักษณะเฉพาะใน SQL Server 2014 เช่นเดียวกับใน SQL Server 2012

การเปรียบเทียบnodes()vs exist()ไม่ยุติธรรมเนื่องจากจะไม่ส่งคืนผลลัพธ์เดียวกันหากมีองค์ประกอบที่ตรงกันมากกว่าหนึ่งรายการใน XML สำหรับหนึ่งแถว exist()จะส่งคืนหนึ่งแถวจากตารางฐานโดยไม่คำนึงถึงในขณะที่nodes()อาจให้คุณมากกว่าหนึ่งแถวที่ส่งคืนสำหรับแต่ละแถวในตารางฐาน
เรารู้ข้อมูล แต่ SQL Server ไม่ได้และต้องสร้างแผนแบบสอบถามที่คำนึงถึงสิ่งนั้น

หากต้องการทำให้nodes()คิวรีเทียบเท่ากับexist()คิวรีคุณสามารถทำสิ่งนี้ได้

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

ด้วยแบบสอบถามเช่นนั้นไม่มีความแตกต่างระหว่างการใช้nodes()หรือexist()ที่เป็นเพราะ SQL Server สร้างแผนเกือบเหมือนกันสำหรับทั้งสองรุ่นที่ไม่ได้ใช้ดัชนีและแผนเดียวกันทุกประการเมื่อใช้ดัชนี นั่นเป็นจริงทั้งใน SQL Server 2012 และ SQL Server 2014

สำหรับฉันใน SQL Server 2012 การสืบค้นที่ไม่มีดัชนี XML ใช้เวลา 6 วินาทีโดยใช้การnodes()สืบค้นที่แก้ไขแล้วข้างต้น ไม่มีความแตกต่างระหว่างการใช้เส้นทางแบบเต็มหรือเส้นทางแบบสั้น ด้วยดัชนี XML ในสถานที่เวอร์ชันพา ธ แบบเต็มจะเร็วที่สุดและใช้เวลา 5 ms และการใช้เส้นทางลัดใช้เวลาประมาณ 500 มิลลิวินาที การตรวจสอบแผนแบบสอบถามจะบอกคุณว่าเหตุใดจึงมีความแตกต่าง แต่รุ่นสั้นคือเมื่อคุณใช้เส้นทางแบบสั้น SQL Server จะค้นหาดัชนีในเส้นทางสั้น (ช่วงค้นหาโดยใช้like) และส่งกลับแถว 700,000 ก่อนทิ้งแถวที่ ไม่ตรงกับค่า เมื่อใช้เส้นทางแบบเต็ม SQL Server สามารถใช้นิพจน์เส้นทางโดยตรงพร้อมกับค่าของโหนดเพื่อทำการค้นหาและส่งคืนแถวที่ 105 เท่านั้นตั้งแต่เริ่มต้นเพื่อทำงาน

การใช้ SQL Server 2014 และตัวประมาณค่าความสำคัญใหม่ไม่มีความแตกต่างในแบบสอบถามเหล่านี้เมื่อใช้ดัชนี XML หากไม่ใช้ดัชนีแบบสอบถามจะยังคงใช้เวลาเท่าเดิม แต่ใช้เวลา 15 วินาที เห็นได้ชัดว่าไม่ใช่การปรับปรุงที่นี่เมื่อใช้สิ่งใหม่

ไม่แน่ใจว่าฉันได้ติดตามคำถามของคุณจริง ๆ หรือไม่ตั้งแต่ฉันแก้ไขคิวรี่ให้เทียบเท่า แต่นี่คือสิ่งที่ฉันเชื่อว่าเป็นตอนนี้

ทำไมnodes()แบบสอบถาม (ฉบับดั้งเดิม) ที่มีดัชนี XML อยู่ในที่ช้าลงอย่างมากเมื่อไม่ใช้ดัชนี

คำตอบก็คือเครื่องมือเพิ่มประสิทธิภาพแผนแบบสอบถาม SQL Server ทำสิ่งที่ไม่ดีและแนะนำผู้ประกอบการสปูล ฉันไม่รู้ว่าทำไม แต่ข่าวดีก็คือมันไม่ได้อยู่ที่นั่นอีกต่อไปด้วยตัวประเมินความสำคัญแบบใหม่ใน SQL Server 2014
ไม่มีดัชนีในสถานที่แบบสอบถามจะใช้เวลาประมาณ 7 วินาทีไม่ว่าจะใช้ตัวประมาณแบบ cardinality ใด ด้วยดัชนีจะใช้เวลา 15 วินาทีกับตัวประมาณค่าเก่า (SQL Server 2012) และประมาณ 2 วินาทีด้วยตัวประมาณใหม่ (SQL Server 2014)

หมายเหตุ: การค้นพบข้างต้นสามารถใช้ได้กับข้อมูลการทดสอบของคุณ อาจมีเรื่องราวที่แตกต่างกันโดยสิ้นเชิงเพื่อบอกว่าคุณเปลี่ยนขนาดรูปร่างหรือรูปแบบของ XML หรือไม่ ไม่มีทางทราบได้อย่างแน่นอนโดยไม่ต้องทดสอบกับข้อมูลที่คุณมีในตาราง

วิธีการที่ดัชนี XML ทำงาน

ดัชนี XML ใน SQL Server ถูกนำมาใช้เป็นตารางภายใน ดัชนี XML หลักสร้างตารางด้วยคีย์หลักของตารางฐานบวกคอลัมน์รหัสโหนดรวม 12 คอลัมน์ มันจะมีหนึ่งแถวต่อหนึ่งelement/node/attribute etc.ตารางเพื่อให้แน่นอนว่าตารางสามารถมีขนาดใหญ่มากขึ้นอยู่กับขนาดของ XML ที่เก็บไว้ ด้วยดัชนี XML หลักแทนที่ SQL Server สามารถใช้คีย์หลักของตารางภายในเพื่อค้นหาโหนดและค่า XML สำหรับแต่ละแถวในตารางฐาน

ดัชนี XML รองมาในสามประเภท เมื่อคุณสร้างดัชนี XML รองจะมีดัชนีที่ไม่ใช่คลัสเตอร์ที่สร้างขึ้นในตารางภายในและขึ้นอยู่กับประเภทของดัชนีรองที่คุณสร้างขึ้นมันจะมีคอลัมน์และคอลัมน์คำสั่งต่างกัน

จากCREATE XML INDEX (Transact-SQL) :

VALUE
สร้างดัชนี XML รองในคอลัมน์ที่คอลัมน์สำคัญคือ (ค่าโหนดและเส้นทาง) ของดัชนี XML หลัก

PATH
สร้างดัชนี XML รองในคอลัมน์ที่สร้างจากค่าพา ธ และค่าโหนดในดัชนี XML หลัก ในดัชนีเส้นทางรองค่าเส้นทางและโหนดเป็นคอลัมน์สำคัญที่อนุญาตการค้นหาที่มีประสิทธิภาพเมื่อค้นหาเส้นทาง

PROPERTY
สร้างดัชนี XML รองบนคอลัมน์ (PK, พา ธ และค่าโหนด) ของดัชนี XML หลักโดยที่ PK คือคีย์หลักของตารางฐาน

ดังนั้นเมื่อคุณสร้างดัชนี PATH คอลัมน์แรกในดัชนีนั้นคือนิพจน์พา ธ และคอลัมน์ที่สองคือค่าในโหนดนั้น ที่จริงแล้วพา ธ จะถูกจัดเก็บในรูปแบบการบีบอัดและย้อนกลับ สิ่งนั้นจะถูกเก็บไว้ตรงกันข้ามคือสิ่งที่ทำให้มีประโยชน์ในการค้นหาโดยใช้นิพจน์พา ธ สั้น ในกรณีที่เส้นทางสั้น ๆ ของคุณที่คุณค้นหา//item/value/@string, และ//item/@name //itemเนื่องจากเส้นทางจะถูกเก็บย้อนกลับในคอลัมน์ SQL Server สามารถใช้ช่วงการค้นหาด้วยlike = '€€€€€€%ที่€€€€€€เป็นเส้นทางย้อนกลับ เมื่อคุณใช้เส้นทางแบบเต็มไม่มีเหตุผลที่จะใช้likeเนื่องจากเส้นทางทั้งหมดจะถูกเข้ารหัสในคอลัมน์และยังสามารถใช้ค่าในเพรดิเคตค้นหาได้

คำถามของคุณ :

เมื่อใดควรสร้างดัชนี XML

เป็นทางเลือกสุดท้ายหากเคย ดีกว่าที่จะออกแบบฐานข้อมูลของคุณเพื่อให้คุณไม่ต้องใช้ค่าใน XML เพื่อกรองในส่วนคำสั่ง หากคุณทราบล่วงหน้าว่าคุณต้องทำเช่นนั้นคุณสามารถใช้การส่งเสริมการขายเพื่อสร้างคอลัมน์จากการคำนวณที่คุณสามารถจัดทำดัชนีหากจำเป็น ตั้งแต่ SQL Server 2012 SP1 คุณยังมีดัชนี XML แบบเลือกได้อีกด้วย การทำงานเบื้องหลังจะค่อนข้างเหมือนกับดัชนี XML ปกติเฉพาะคุณเท่านั้นที่ระบุนิพจน์พา ธ ในการกำหนดดัชนีและเฉพาะโหนดที่ตรงกันเท่านั้นที่ได้รับการจัดทำดัชนี ด้วยวิธีนี้คุณสามารถประหยัดพื้นที่ได้มาก

ทำไม. nodes () ที่มีดัชนีแย่กว่าไม่มี

เมื่อมีดัชนี XML สร้างขึ้นในตาราง SQL Server จะใช้ดัชนีนั้น (ตารางภายใน) เพื่อรับข้อมูลเสมอ การตัดสินใจนั้นจะกระทำก่อนที่เครื่องมือเพิ่มประสิทธิภาพจะพูดในสิ่งที่เร็วและอะไรที่ไม่เร็ว อินพุตไปยังเครื่องมือเพิ่มประสิทธิภาพจะถูกเขียนใหม่ดังนั้นจึงใช้ตารางภายในและหลังจากนั้นขึ้นอยู่กับเครื่องมือเพิ่มประสิทธิภาพในการทำสิ่งที่ดีที่สุดเช่นเดียวกับแบบสอบถามทั่วไป เมื่อไม่มีการใช้ดัชนีจะมีฟังก์ชั่นที่มีค่าเป็นตารางจำนวนหนึ่งที่ใช้แทน บรรทัดล่างคือคุณไม่สามารถบอกสิ่งที่จะเร็วขึ้นโดยไม่ต้องทดสอบ

เราจะหลีกเลี่ยงผลกระทบด้านลบได้อย่างไร?

การทดสอบ


2
ความคิดของคุณเกี่ยวกับความแตกต่าง.nodes()และ.exist()น่าเชื่อถือ นอกจากนี้ความจริงที่ว่าดัชนีด้วยfull path searchเร็วขึ้นนั้นดูเหมือนจะเข้าใจง่าย นี้จะหมายถึง: หากคุณสร้างดัชนี XML คุณต้องเสมอจะตระหนักถึงอิทธิพลเชิงลบกับ XPath ทั่วไปใด ๆ ( //หรือ*หรือ..หรือ[filter]หรืออะไรที่ไม่ธรรมดาเพียง Xpath ... ) ในความเป็นจริงคุณควรใช้เส้นทางแบบเต็มเท่านั้น - ค่อนข้างดีเสมอ ...
Shnugo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.