XML इंडेक्स के साथ बहुत ही अजीब प्रदर्शन


32

मेरा प्रश्न इस पर आधारित है: https://stackoverflow.com/q/35575990/5089204

उत्तर देने के लिए मैंने निम्नलिखित परीक्षण-परिदृश्य किया।

परीक्षण परिदृश्य

पहले मैं एक परीक्षण तालिका बनाता हूं और इसे 100.000 पंक्तियों के साथ भरता हूं। एक यादृच्छिक संख्या (0 से 1000) को प्रत्येक यादृच्छिक संख्या के लिए ~ 100 पंक्तियों तक ले जाना चाहिए। यह संख्या एक varchar col में और आपके XML में मान के रूप में डाली जाती है।

फिर मैं ओपी की तरह एक कॉल करता हूं। इसे दूसरे के लिए एक छोटे से लाभ के साथ .exist () और .nodes () के साथ चाहिए, लेकिन दोनों को 5 से 6 सेकंड लगते हैं। वास्तव में मैं दो बार कॉल करता हूं: स्वैप किए गए क्रम में दूसरी बार और कैश्ड परिणामों या योजनाओं के माध्यम से झूठी सकारात्मकता से बचने के लिए पूर्ण पथ के बजाय "// आइटम" के साथ थोड़े बदले हुए खोज पाराम के साथ।

फिर मैं एक XML इंडेक्स बनाता हूं और वही कॉल करता हूं

अब - वास्तव में मुझे क्या आश्चर्य हुआ! - पूर्ण पथ के .nodesसाथ पहले (9 सेकंड) की तुलना में बहुत धीमा है, लेकिन आधे से एक सेकंड तक नीचे है , पूर्ण पथ के साथ भी लगभग 0.10 सेकंड तक नीचे है। (जबकि साथ लघु पथ बेहतर है, लेकिन अभी भी बहुत पीछे ).exist().nodes().exist()

प्रशन:

मेरे स्वयं के परीक्षण संक्षिप्त रूप में सामने आते हैं: XML इंडेक्स एक डेटाबेस को अत्यधिक उड़ा सकता है। वे चीजों को बहुत तेज कर सकते हैं (2 संपादित करें), लेकिन आपके प्रश्नों को भी धीमा कर सकते हैं। मैं यह समझना चाहता हूं कि वे कैसे काम करते हैं ... किसी को XML इंडेक्स कब बनाना चाहिए? .nodes()सूचकांक के बिना के साथ बदतर क्यों हो सकता है? नकारात्मक प्रभाव से कैसे बचा जा सकता है?

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 - परिणाम

यह SQL सर्वर 2012 के साथ एक परिणाम है जो स्थानीय रूप से एक मध्यम लैपटॉप पर स्थापित है। इस परीक्षण में मैं उस पर अत्यधिक नकारात्मक प्रभाव को पुन: उत्पन्न नहीं कर सका 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

EDIT 2 बड़े XML के साथ टेस्ट

टीटी के सुझाव के अनुसार मैंने ऊपर XML का उपयोग किया था, लेकिन item-nodes की प्रतिलिपि लगभग 450 वस्तुओं तक पहुंचने के लिए। मैंने एक्सएमएल में हिट-नोड को बहुत अधिक होने दिया ('क्योंकि मुझे लगता है कि .exist()पहली हिट पर रुक जाएगा, जबकि .nodes()जारी रहेगा)

XML-index बनाने ने mdf-file को ~ 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 के बीच के समय में अंतर SQL Server 2014 में नए कार्डिनैलिटी एस्टीमेटर के कारण होता है। आप पुराने अनुमानक को बाध्य करने के लिए SQL Server 2014 में ट्रेस फ़्लैग का उपयोग कर सकते हैं और फिर आप उसी समय को देखेंगे SQL Server 2012 में SQL Server 2012 की विशेषताएँ।

nodes()बनाम exist()की तुलना करना उचित नहीं है क्योंकि वे एक ही परिणाम नहीं लौटाएंगे यदि एक पंक्ति में एक से अधिक मिलान तत्व XML में हों। exist()बेस टेबल से एक पंक्ति की परवाह किए बिना वापस आ जाएगी, जबकि nodes()संभवतः आपको बेस टेबल में प्रत्येक पंक्ति के लिए एक से अधिक पंक्ति लौटा सकती है।
हम डेटा को जानते हैं लेकिन SQL सर्वर को क्वेरी प्लान बनाने की आवश्यकता नहीं है जो इसे ध्यान में रखता है।

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 सर्वर इंडेक्स का उपयोग नहीं करने वाले दो संस्करणों के लिए लगभग एक ही योजना बनाता है और जब इंडेक्स का उपयोग किया जाता है तो एक ही योजना। यह SQL Server 2012 और SQL Server 2014 के लिए सही है।

SQL Server 2012 में मेरे लिए XML इंडेक्स के बिना क्वेरीज़ को क्वेरी के संशोधित संस्करण का उपयोग करते हुए 6 सेकंड nodes()लगते हैं। पूर्ण पथ या छोटे पथ का उपयोग करने के बीच कोई अंतर नहीं है। एक्सएमएल इंडेक्स के साथ पूर्ण पथ संस्करण सबसे तेज़ है और 5 एमएस लेता है और छोटे पथ का उपयोग करने में लगभग 500 एमएस लगता है। क्वेरी योजनाओं की जांच करने से आपको पता चलेगा कि अंतर क्यों है, लेकिन संक्षिप्त संस्करण यह है कि जब आप एक छोटे पथ का उपयोग करते हैं, तो SQL सर्वर लघु पथ पर अनुक्रमणिका (एक सीमा का उपयोग करने वाला like) की तलाश करता है और पंक्तियों को त्यागने से पहले 700000 पंक्तियों को लौटाता है मूल्य पर मेल नहीं खाते। पूर्ण पथ का उपयोग करते समय, SQL सर्वर चाहने के लिए नोड के मान के साथ सीधे पथ अभिव्यक्ति का उपयोग कर सकता है और स्क्रैच से केवल 105 पंक्तियों को काम पर लौटाता है।

SQL सर्वर 2014 और नए कार्डिनल्टी अनुमानक का उपयोग करते हुए, XML इंडेक्स का उपयोग करते समय इन प्रश्नों में कोई अंतर नहीं होता है। इंडेक्स का उपयोग किए बिना क्वेरीज़ अभी भी समान समय लेती है लेकिन यह 15 सेकंड है। स्पष्ट रूप से नए सामान का उपयोग करते समय यहां सुधार नहीं हुआ है।

यकीन नहीं होता कि मैं पूरी तरह से खो गया हूं कि आपके सवाल वास्तव में क्या हैं क्योंकि मैंने प्रश्नों को समतुल्य होने के लिए संशोधित किया था, लेकिन यहां मैं मानता हूं कि यह अब है।

nodes()किसी XML इंडेक्स के साथ क्वेरी (मूल संस्करण) क्यों काफी धीमी हो जाती है, जब एक इंडेक्स का उपयोग नहीं किया जाता है?

खैर, जवाब यह है कि SQL सर्वर क्वेरी प्लान ऑप्टिमाइज़र कुछ बुरा करता है और एक स्पूल ऑपरेटर को पेश कर रहा है। मुझे पता नहीं क्यों लेकिन अच्छी खबर यह है कि यह SQL Server 2014 में नए कार्डिनलिटी अनुमानक के साथ अब नहीं है।
जगह में कोई इंडेक्स नहीं होने के कारण क्वेरी के बारे में 7 सेकंड लगते हैं चाहे कार्डिनैलिटी अनुमानक का उपयोग किया जाए। सूचकांक के साथ पुराने अनुमानक (SQL Server 2012) के साथ 15 सेकंड और नए अनुमानक (SQL Server 2014) के साथ लगभग 2 सेकंड लगते हैं।

नोट: उपरोक्त निष्कर्ष आपके परीक्षण डेटा के साथ मान्य हैं। एक्सएमएल के आकार, आकार या रूप को बदलने पर बताने के लिए एक पूरी अलग कहानी हो सकती है। तालिका में आपके पास वास्तव में मौजूद डेटा के परीक्षण के बिना सुनिश्चित करने का कोई तरीका नहीं है।

XML इंडेक्स कैसे काम करता है

SQL सर्वर में XML इंडेक्स को आंतरिक तालिकाओं के रूप में कार्यान्वित किया जाता है। प्राथमिक XML इंडेक्स कुल 12 कॉलम में बेस टेबल प्लस नोड आईडी कॉलम की प्राथमिक कुंजी के साथ तालिका बनाता है। इसमें प्रति पंक्ति एक पंक्ति होगी element/node/attribute etc.ताकि तालिका संग्रहीत XML के आकार के आधार पर निश्चित रूप से बड़ी हो सके। एक प्राथमिक XML इंडेक्स के साथ SQL सर्वर बेस टेबल में प्रत्येक पंक्ति के लिए XML नोड्स और मानों का पता लगाने के लिए आंतरिक तालिका की प्राथमिक कुंजी का उपयोग कर सकता है।

माध्यमिक XML सूचकांक तीन प्रकारों में आते हैं। जब आप एक माध्यमिक XML इंडेक्स बनाते हैं, तो आंतरिक टेबल पर एक गैर-क्लस्टर इंडेक्स बनाया जाता है और आप किस प्रकार का सेकेंडरी इंडेक्स बनाते हैं, उसके आधार पर इसमें अलग-अलग कॉलम और कॉलम ऑर्डर होंगे।

से एक्सएमएल सूचकांक (Transact-SQL) बनाने :

VALUE
उन स्तंभों पर एक द्वितीयक XML अनुक्रमणिका बनाता है जहां प्राथमिक स्तंभ अनुक्रमणिका के प्रमुख स्तंभ (नोड मान और पथ) हैं।

पथ
प्राथमिक XML इंडेक्स में पथ मान और नोड मान पर निर्मित स्तंभों पर एक द्वितीयक XML इंडेक्स बनाता है। पथ माध्यमिक सूचकांक में, पथ और नोड मान मुख्य स्तंभ हैं जो पथ की खोज करते समय कुशल खोज की अनुमति देते हैं।

संपत्ति
प्राथमिक XML सूचकांक के कॉलम (पीके, पथ और नोड मूल्य) पर एक माध्यमिक XML सूचकांक बनाता है जहां पीके आधार तालिका की प्राथमिक कुंजी है।

इसलिए जब आप एक PATH इंडेक्स बनाते हैं, तो उस इंडेक्स में पहला कॉलम पाथ एक्सप्रेशन होता है और दूसरा कॉलम उस नोड में वैल्यू होता है। दरअसल, मार्ग एक प्रकार के संपीड़ित प्रारूप में संग्रहीत होता है और उलट होता है। यह उलट संग्रहीत किया जाता है कि यह छोटी पथ अभिव्यक्तियों का उपयोग करके खोजों में क्या उपयोगी बनाता है। अपने छोटे पथ के मामले में आपने खोज की //item/value/@string, //item/@nameऔर //item। चूंकि पथ स्तंभ में उल्टा संग्रहीत है, इसलिए SQL सर्वर एक रेंज की तलाश के साथ उपयोग कर सकता है like = '€€€€€€%जहां €€€€€€पथ उलट है। जब आप एक पूर्ण पथ का उपयोग करते हैं, तो इसका उपयोग करने का कोई कारण नहीं है likeक्योंकि संपूर्ण पथ स्तंभ में एन्कोडेड है और मूल्य का उपयोग सीकड विधेयकों में भी किया जा सकता है।

आपके प्रश्न :

XML इंडेक्स इंडेक्स कब बनाना चाहिए?

एक अंतिम उपाय के रूप में यदि कभी। अपने डेटाबेस को डिजाइन करने के लिए बेहतर है ताकि आपको एक्सएमएल के अंदर मूल्यों का उपयोग करने के लिए जहां एक खंड में फ़िल्टर न करना पड़े। यदि आप पहले से जानते हैं कि आपको ऐसा करने की आवश्यकता है, तो आप एक संगणित कॉलम बनाने के लिए संपत्ति संवर्धन का उपयोग कर सकते हैं जिसे आप ज़रूरत पड़ने पर अनुक्रमित कर सकते हैं। SQL Server 2012 SP1 के बाद से, आपके पास चुनिंदा XML इंडेक्स भी उपलब्ध हैं। नियमित एक्सएमएल इंडेक्स के साथ दृश्य के पीछे कामकाज बहुत अधिक है, केवल आप इंडेक्स परिभाषा में पथ अभिव्यक्ति निर्दिष्ट करते हैं और केवल नोड्स जो मैच अनुक्रमित होते हैं। इस तरह आप बहुत सी जगह बचा सकते हैं।

इंडेक्स के साथ .nodes () बिना से ज्यादा खराब क्यों हो सकता है?

जब एक टेबल पर XML इंडेक्स बनाया जाता है, तो SQL सर्वर डेटा प्राप्त करने के लिए हमेशा उस इंडेक्स (आंतरिक टेबल) का उपयोग करेगा। ऑप्टिमाइज़र ने जो कहा है, जो तेज़ है और जो तेज़ नहीं है, उससे पहले वह फैसला कर चुका होता है। ऑप्टिमाइज़र के इनपुट को फिर से लिखा गया है, इसलिए यह आंतरिक तालिकाओं का उपयोग कर रहा है और इसके बाद यह ऑप्टिमाइज़र पर निर्भर है कि वह नियमित क्वेरी के साथ अपना सर्वश्रेष्ठ प्रदर्शन करे। जब कोई इंडेक्स उपयोग नहीं किया जाता है, तो टेबल-वेल्यू किए गए कुछ फ़ंक्शन होते हैं जो इसके बजाय उपयोग किए जाते हैं। लब्बोलुआब यह है कि आप यह नहीं बता सकते हैं कि परीक्षण के बिना क्या तेजी से होगा।

नकारात्मक प्रभाव से कोई कैसे बच सकता है?

परिक्षण


2
के अंतर के बारे में आपके विचार .nodes()और .exist()आश्वस्त हैं। इस तथ्य के साथ कि सूचकांक full path searchतेजी से समझने में आसान लगता है। इसका मतलब यह होगा: आप एक एक्सएमएल सूचकांक बनाते हैं, तो आप चाहिए हमेशा किसी भी सामान्य XPath (साथ नकारात्मक प्रभाव के बारे में पता होना //या *या ..या [filter]या कुछ भी न सिर्फ सादा Xpath ...)। वास्तव में आपको केवल पूर्ण पथ का उपयोग करना चाहिए - काफी शानदार बैक ड्रॉ ...
शन्नू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.