यदि आप केवल-पढ़ने के लिए फ़ाइल समूह पर संग्रहीत एक या अधिक विभाजन के साथ तालिका विभाजन का उपयोग करते हैं, तो SQL अपडेट और डिलीट स्टेटमेंट एक त्रुटि के साथ विफल हो सकते हैं। बेशक, यह अपेक्षित व्यवहार है यदि किसी भी संशोधन के लिए केवल-पढ़ने के लिए फ़ाइल समूह को लिखने की आवश्यकता होगी; हालांकि इस त्रुटि की स्थिति का सामना करना भी संभव है जहां परिवर्तन पढ़ने-लिखने के रूप में चिह्नित फ़ाइल समूहों तक ही सीमित हैं।
नमूना डेटाबेस
समस्या को प्रदर्शित करने के लिए, हम एक एकल कस्टम फ़ाइल समूह के साथ एक साधारण डेटाबेस बनाएंगे जिसे हम बाद में केवल-पढ़ने के लिए चिह्नित करेंगे। ध्यान दें कि आपको अपने परीक्षण उदाहरण के अनुरूप फ़ाइल नाम पथ में जोड़ना होगा।
USE master; GO CREATE DATABASE Test; GO -- This filegroup will be marked read-only later ALTER DATABASE Test ADD FILEGROUP ReadOnlyFileGroup; GO -- Add a file to the new filegroup ALTER DATABASE Test ADD FILE ( NAME = 'Test_RO', FILENAME = '<...your path...>\MSSQL\DATA\Test_ReadOnly.ndf' ) TO FILEGROUP ReadOnlyFileGroup;
विभाजन कार्य और योजना
अब हम एक बुनियादी विभाजन फ़ंक्शन और योजना बनाएंगे जो डेटा के साथ पंक्तियों को निर्देशित करेगा 1 जनवरी 2000 से पहले केवल-पढ़ने के लिए विभाजन के लिए। बाद में डेटा को रीड-राइट प्राइमरी फाइलग्रुप में रखा जाएगा:
USE Test; GO CREATE PARTITION FUNCTION PF (datetime) AS RANGE RIGHT FOR VALUES ({D '2000-01-01'}); GO CREATE PARTITION SCHEME PS AS PARTITION PF TO (ReadOnlyFileGroup, [PRIMARY]);
रेंज राइट स्पेसिफिकेशन का मतलब है कि 1 जनवरी 2000 की सीमा मान वाली पंक्तियाँ रीड-राइट विभाजन में होंगी।
विभाजित तालिका और अनुक्रमणिका
अब हम अपनी टेस्ट टेबल बना सकते हैं:
CREATE TABLE dbo.Test ( dt datetime NOT NULL, c1 integer NOT NULL, c2 integer NOT NULL, CONSTRAINT PK_dbo_Test__c1_dt PRIMARY KEY CLUSTERED (dt) ON PS (dt) ) ON PS (dt); GO CREATE NONCLUSTERED INDEX IX_dbo_Test_c1 ON dbo.Test (c1) ON PS (dt); GO CREATE NONCLUSTERED INDEX IX_dbo_Test_c2 ON dbo.Test (c2) ON PS (dt);
तालिका में डेटाटाइम कॉलम पर क्लस्टर प्राथमिक कुंजी है, और उस कॉलम पर भी विभाजित है। अन्य दो पूर्णांक स्तंभों पर गैर-संकुल अनुक्रमणिकाएँ हैं, जो उसी तरह विभाजित हैं (सूचकांक आधार तालिका के साथ संरेखित हैं)।
नमूना डेटा
अंत में, हम उदाहरण डेटा की कुछ पंक्तियाँ जोड़ते हैं, और 2000 से पहले के डेटा विभाजन को केवल पढ़ने के लिए बनाते हैं:
INSERT dbo.Test WITH (TABLOCKX) (dt, c1, c2) VALUES ({D '1999-12-31'}, 1, 1), -- Read only ({D '2000-01-01'}, 2, 2); -- Writable GO ALTER DATABASE Test MODIFY FILEGROUP ReadOnlyFileGroup READ_ONLY;
आप निम्न परीक्षण अद्यतन कथनों का उपयोग यह पुष्टि करने के लिए कर सकते हैं कि केवल-पढ़ने के लिए विभाजन में डेटा को संशोधित नहीं किया जा सकता है, जबकि dt
वाले डेटा को संशोधित नहीं किया जा सकता है। 1 जनवरी 2000 को या उसके बाद का मान इस पते पर लिखा जा सकता है:
-- Will fail, as expected UPDATE dbo.Test SET c2 = 1 WHERE dt = {D '1999-12-31'}; -- Will succeed, as expected UPDATE dbo.Test SET c2 = 999 WHERE dt = {D '2000-01-01'}; -- Reset the value of c2 UPDATE dbo.Test SET c2 = 2 WHERE dt = {D '2000-01-01'};का मान रीसेट करें
एक अप्रत्याशित विफलता
हमारे पास दो पंक्तियाँ हैं:एक केवल-पढ़ने के लिए (1999-12-31); और एक पढ़ना-लिखना (2000-01-01):
अब निम्न क्वेरी का प्रयास करें। यह उसी लिखने योग्य "2000-01-01" पंक्ति की पहचान करता है जिसे हमने अभी सफलतापूर्वक अपडेट किया है, लेकिन एक अलग का उपयोग करता है जहां क्लॉज प्रेडिकेट करता है:
UPDATE dbo.Test SET c2 = 2 WHERE c1 = 2;
अनुमानित (पूर्व-निष्पादन) योजना है:
इस चर्चा के लिए चार (!) कंप्यूट स्केलर महत्वपूर्ण नहीं हैं। उनका उपयोग यह निर्धारित करने के लिए किया जाता है कि क्लस्टर्ड इंडेक्स अपडेट ऑपरेटर पर आने वाली प्रत्येक पंक्ति के लिए गैर-संकुल सूचकांक को बनाए रखने की आवश्यकता है या नहीं।
अधिक दिलचस्प बात यह है कि यह अद्यतन कथन विफल . है इसी तरह की त्रुटि के साथ:
संदेश 652, स्तर 16, राज्य 1तालिका "dbo.Test" (RowsetId 72057594039042048) के लिए "PK_dbo_Test__c1_dt" अनुक्रमणिका केवल-पढ़ने के लिए फ़ाइल समूह ("ReadOnlyFileGroup") पर स्थित है, जिसे संशोधित नहीं किया जा सकता है।
विभाजन उन्मूलन नहीं
यदि आपने पहले विभाजन के साथ काम किया है, तो आप सोच रहे होंगे कि 'विभाजन उन्मूलन' इसका कारण हो सकता है। तर्क कुछ इस तरह होगा:
पिछले बयानों में, जहां क्लॉज में विभाजन कॉलम के लिए एक शाब्दिक मूल्य प्रदान किया गया था, इसलिए SQL सर्वर तुरंत यह निर्धारित करने में सक्षम होगा कि किस विभाजन को एक्सेस करना है। जहां क्लॉज को बदलकर अब पार्टिशनिंग कॉलम का संदर्भ नहीं दिया जाता है, हमने SQL सर्वर को क्लस्टर्ड इंडेक्स स्कैन का उपयोग करके हर पार्टीशन तक पहुंचने के लिए मजबूर कर दिया है।
सामान्य तौर पर, यह सब सच है, लेकिन यही कारण नहीं है कि यहां अपडेट स्टेटमेंट विफल हो जाता है।
अपेक्षित व्यवहार यह है कि SQL सर्वर पढ़ने . में सक्षम होना चाहिए क्वेरी निष्पादन के दौरान किसी भी और सभी विभाजनों से। डेटा संशोधन कार्रवाई केवल विफल होनी चाहिए यदि निष्पादन इंजन वास्तव में संशोधित करने का प्रयास करता है केवल-पढ़ने के लिए फ़ाइल समूह पर संग्रहीत एक पंक्ति।
उदाहरण के लिए, आइए पिछली क्वेरी में एक छोटा सा बदलाव करें:
UPDATE dbo.Test SET c2 = 2, dt = dt WHERE c1 = 2;
जहां क्लॉज बिल्कुल पहले जैसा ही है। फर्क सिर्फ इतना है कि अब हम (जानबूझकर) विभाजन कॉलम को अपने बराबर सेट कर रहे हैं। यह उस कॉलम में संग्रहीत मान को नहीं बदलेगा, लेकिन यह परिणाम को प्रभावित करता है। अपडेट अब सफल (यद्यपि अधिक जटिल निष्पादन योजना के साथ):
ऑप्टिमाइज़र ने नए स्प्लिट, सॉर्ट, और संक्षिप्त ऑपरेटरों को पेश किया है, और प्रत्येक संभावित रूप से प्रभावित गैर-क्लस्टर इंडेक्स को अलग से बनाए रखने के लिए आवश्यक मशीनरी को जोड़ा है (विस्तृत, या प्रति-सूचकांक रणनीति का उपयोग करके)।
संकुल अनुक्रमणिका स्कैन गुण दर्शाते हैं कि दोनों विभाजन पढ़ने के दौरान तालिका का उपयोग किया गया:
इसके विपरीत, क्लस्टर्ड इंडेक्स अपडेट से पता चलता है कि केवल पढ़ने-लिखने वाले विभाजन को लिखने के लिए एक्सेस किया गया था:
प्रत्येक गैर-संकुल सूचकांक अद्यतन ऑपरेटर समान जानकारी दिखाता है:रन टाइम पर केवल लिखने योग्य विभाजन (#2) को संशोधित किया गया था, इसलिए कोई त्रुटि नहीं हुई।
कारण का खुलासा
नई योजना सफल होती है नहीं क्योंकि गैर-संकुलित अनुक्रमित अलग से बनाए रखा जाता है; न ही क्या यह (सीधे) स्प्लिट-सॉर्ट-संक्षिप्त संयोजन के कारण अद्वितीय अनुक्रमणिका में क्षणिक डुप्लिकेट कुंजी त्रुटियों से बचने के लिए आवश्यक है।
वास्तविक कारण कुछ ऐसा है जिसका मैंने अपने पिछले लेख "ऑप्टिमाइज़िंग अपडेट क्वेरीज़" में संक्षेप में उल्लेख किया था - एक आंतरिक अनुकूलन जिसे रोसेट शेयरिंग के रूप में जाना जाता है। . जब इसका उपयोग किया जाता है, तो क्लस्टर्ड इंडेक्स अपडेट प्लान के रीडिंग साइड पर क्लस्टर्ड इंडेक्स स्कैन, सीक या की लुकअप के समान अंतर्निहित स्टोरेज इंजन रोसेट को साझा करता है।
रोसेट शेयरिंग ऑप्टिमाइज़ेशन के साथ, SQL सर्वर ऑफ़लाइन या रीड-ओनली फ़ाइलग्रुप के लिए जाँच करता है पढ़ते समय। उन योजनाओं में जहां क्लस्टर्ड इंडेक्स अपडेट एक अलग रोसेट का उपयोग करता है, ऑफलाइन/रीड-ओनली चेक केवल अपडेट (या डिलीट) इटरेटर पर प्रत्येक पंक्ति के लिए किया जाता है।
गैर-दस्तावेज समाधान
आइए पहले मज़ेदार, आकर्षक, लेकिन अव्यावहारिक चीज़ों को हटा दें।
साझा रोसेट ऑप्टिमाइज़ेशन केवल तभी लागू किया जा सकता है जब क्लस्टर्ड इंडेक्स से रूट खोज, स्कैन, या कुंजी लुकअप एक पाइपलाइन हो . किसी भी ब्लॉकिंग या सेमी-ब्लॉकिंग ऑपरेटरों की अनुमति नहीं है। एक और तरीका रखो, अगली पंक्ति पढ़ने से पहले प्रत्येक पंक्ति को पढ़ने के स्रोत से लिखने के गंतव्य तक पहुंचने में सक्षम होना चाहिए।
एक अनुस्मारक के रूप में, विफल . के लिए नमूना डेटा, विवरण और निष्पादन योजना यहां दी गई है फिर से अपडेट करें:
--Change the read-write row UPDATE dbo.Test SET c2 = 2 WHERE c1 = 2;
हैलोवीन सुरक्षा
ब्लॉकिंग ऑपरेटर को योजना से परिचित कराने का एक तरीका इस अपडेट के लिए स्पष्ट हैलोवीन प्रोटेक्शन (एचपी) की आवश्यकता है। ब्लॉकिंग ऑपरेटर के साथ रीड को राइट से अलग करने से रोसेट शेयरिंग ऑप्टिमाइज़ेशन का उपयोग होने से रोका जा सकेगा (कोई पाइपलाइन नहीं)। गैर-दस्तावेज और असमर्थित (केवल परीक्षण प्रणाली!) ट्रेस ध्वज 8692 स्पष्ट एचपी के लिए एक उत्सुक टेबल स्पूल जोड़ता है:
-- Works (explicit HP) UPDATE dbo.Test SET c2 = 2 WHERE c1 = 2 OPTION (QUERYTRACEON 8692);
वास्तविक निष्पादन योजना (उपलब्ध है क्योंकि त्रुटि अब फेंकी नहीं गई है) है:
पिछले सफल अपडेट में देखा गया स्प्लिट-सॉर्ट-संक्षिप्त संयोजन में सॉर्ट उस उदाहरण में रोसेट साझाकरण को अक्षम करने के लिए आवश्यक अवरोध प्रदान करता है।
एंटी-रोसेट शेयरिंग ट्रेस फ्लैग
एक और अनियंत्रित ट्रेस ध्वज है जो रोसेट साझाकरण अनुकूलन को अक्षम करता है। इसका संभावित-महंगे अवरोधक ऑपरेटर को पेश नहीं करने का लाभ है। यह निश्चित रूप से अभ्यास में उपयोग नहीं किया जा सकता है (जब तक कि आप Microsoft समर्थन से संपर्क नहीं करते हैं और आपको इसे सक्षम करने की सिफारिश करने के लिए लिखित रूप में कुछ नहीं मिलता है, मुझे लगता है)। फिर भी, मनोरंजन के उद्देश्य से, यहाँ ट्रेस फ़्लैग 8746 कार्रवाई में है:
-- Works (no rowset sharing) UPDATE dbo.Test SET c2 = 2 WHERE c1 = 2 OPTION (QUERYTRACEON 8746);
उस कथन के लिए वास्तविक निष्पादन योजना है:
यहां अंतर के बारे में खुद को समझाने के लिए विभिन्न मूल्यों के साथ प्रयोग करने के लिए स्वतंत्र महसूस करें (वे जो वास्तव में संग्रहीत मूल्यों को बदलते हैं)। जैसा कि मेरी पिछली पोस्ट में बताया गया है, आप निष्पादन योजना में रोसेट शेयरिंग प्रॉपर्टी को बेनकाब करने के लिए अनिर्दिष्ट ट्रेस फ्लैग 8666 का भी उपयोग कर सकते हैं।
यदि आप एक डिलीट स्टेटमेंट के साथ रोसेट शेयरिंग एरर देखना चाहते हैं, तो बस अपडेट को बदलें और क्लॉज को डिलीट के साथ सेट करें, उसी क्लॉज का उपयोग करते हुए।
समर्थित समाधान
यह सुनिश्चित करने के कई संभावित तरीके हैं कि ट्रेस फ़्लैग का उपयोग किए बिना वास्तविक दुनिया के प्रश्नों में रोसेट साझाकरण लागू नहीं किया जाता है। अब जब आप जानते हैं कि मुख्य मुद्दे के लिए एक साझा और पाइपलाइन क्लस्टर इंडेक्स पढ़ने और लिखने की योजना की आवश्यकता है, तो आप शायद अपनी खुद की योजना बना सकते हैं। फिर भी, ऐसे कुछ उदाहरण हैं जो यहां विशेष रूप से देखने लायक हैं।
फोर्स्ड इंडेक्स / कवरिंग इंडेक्स
एक स्वाभाविक विचार यह है कि योजना के पठन पक्ष को संकुल सूचकांक के बजाय गैर-संकुल सूचकांक का उपयोग करने के लिए मजबूर किया जाए। हम लिखित रूप में परीक्षण क्वेरी में सीधे एक इंडेक्स संकेत नहीं जोड़ सकते हैं, लेकिन तालिका को अलियासिंग करने से इसकी अनुमति मिलती है:
UPDATE T SET c2 = 2 FROM dbo.Test AS T WITH (INDEX(IX_dbo_Test_c1)) WHERE c1 = 2;
ऐसा लग सकता है कि समाधान क्वेरी ऑप्टिमाइज़र को पहले स्थान पर चुनना चाहिए था, क्योंकि हमारे पास एक गैर-संकुल सूचकांक है जहां क्लॉज कॉलम c1 की भविष्यवाणी करता है। निष्पादन योजना दर्शाती है कि अनुकूलक ने जैसा चुना वैसा क्यों चुना:
की लुकअप की लागत ऑप्टिमाइज़र को पढ़ने के लिए क्लस्टर्ड इंडेक्स का उपयोग करने के लिए मनाने के लिए पर्याप्त है। कॉलम c2 का वर्तमान मान प्राप्त करने के लिए लुकअप की आवश्यकता होती है, इसलिए कंप्यूट स्केलर यह तय कर सकते हैं कि क्या गैर-संकुल सूचकांक को बनाए रखने की आवश्यकता है।
गैर-संकुल अनुक्रमणिका (कुंजी या शामिल) में स्तंभ c2 जोड़ने से समस्या से बचा जा सकेगा। ऑप्टिमाइज़र क्लस्टर्ड इंडेक्स के बजाय अब कवरिंग इंडेक्स को चुनेगा।
उस ने कहा, यह अनुमान लगाना हमेशा संभव नहीं होता है कि किन स्तंभों की आवश्यकता होगी, या उन सभी को शामिल करना, भले ही सेट ज्ञात हो। याद रखें, कॉलम की आवश्यकता है क्योंकि c2 सेट क्लॉज . में है अद्यतन कथन का। यदि क्वेरी तदर्थ हैं (उदाहरण के लिए उपयोगकर्ताओं द्वारा सबमिट की गई या किसी टूल द्वारा जेनरेट की गई), तो प्रत्येक गैर-क्लस्टर इंडेक्स को इसे एक मजबूत विकल्प बनाने के लिए सभी कॉलम शामिल करने की आवश्यकता होगी।
ऊपर की लुकअप वाली योजना के बारे में एक दिलचस्प बात यह है कि यह नहीं . है त्रुटि उत्पन्न करें। यह एक साझा रोसेट का उपयोग करते हुए की लुकअप और क्लस्टर्ड इंडेक्स अपडेट के बावजूद है। इसका कारण यह है कि गैर-संकुल अनुक्रमणिका सीक c1 =2 पहले के साथ पंक्ति का पता लगाता है कुंजी लुकअप संकुल अनुक्रमणिका को स्पर्श करता है। ऑफ़लाइन / केवल-पढ़ने के लिए फ़ाइल समूह के लिए साझा रोसेट जांच अभी भी लुकअप पर की जाती है, लेकिन यह केवल-पढ़ने के लिए विभाजन को स्पर्श नहीं करता है, इसलिए कोई त्रुटि नहीं डाली जाती है। रुचि के अंतिम (संबंधित) बिंदु के रूप में, ध्यान दें कि इंडेक्स सीक दोनों विभाजनों को छूता है, लेकिन की लुकअप केवल एक को हिट करता है।
केवल-पढ़ने के लिए विभाजन को छोड़कर
एक तुच्छ समाधान विभाजन उन्मूलन पर भरोसा करना है ताकि योजना का पठन पक्ष कभी भी केवल-पढ़ने के लिए विभाजन को न छूए। यह एक स्पष्ट विधेय के साथ किया जा सकता है, उदाहरण के लिए इनमें से कोई भी:
UPDATE dbo.Test SET c2 = 2 WHERE c1 = 2 AND dt >= {D '2000-01-01'}; UPDATE dbo.Test SET c2 = 2 WHERE c1 = 2 AND $PARTITION.PF(dt) > 1; -- Not partition #1
जहां विभाजन-उन्मूलन विधेय को जोड़ने के लिए प्रत्येक क्वेरी को बदलना असंभव या असुविधाजनक है, वहां अन्य समाधान जैसे कि एक दृश्य के माध्यम से अद्यतन करना उपयुक्त हो सकता है। उदाहरण के लिए:
CREATE VIEW dbo.TestWritablePartitions WITH SCHEMABINDING AS -- Only the writable portion of the table SELECT T.dt, T.c1, T.c2 FROM dbo.Test AS T WHERE $PARTITION.PF(dt) > 1; GO -- Succeeds UPDATE dbo.TestWritablePartitions SET c2 = 2 WHERE c1 = 2;
किसी दृश्य का उपयोग करने का एक नुकसान यह है कि आधार तालिका के केवल-पठन भाग को लक्षित करने वाला कोई अद्यतन या हटाना त्रुटि के साथ विफल होने के बजाय, बिना पंक्तियों के प्रभावित होने के सफल होगा। टेबल या दृश्य पर ट्रिगर के बजाय कुछ स्थितियों में इसके लिए एक समाधान हो सकता है, लेकिन यह और भी समस्याएं पेश कर सकता है...लेकिन मैं पीछे हट जाता हूं।
जैसा कि पहले उल्लेख किया गया है, कई संभावित समर्थित समाधान हैं। इस लेख का उद्देश्य यह दिखाना है कि कैसे रोसेट साझाकरण के कारण अप्रत्याशित अद्यतन त्रुटि हुई।