विशाल तालिका में धीमी गति से प्रदर्शन सम्मिलित करना


9

हमारे पास एक प्रक्रिया है जो स्टोर से डेटा लेती है और कंपनी-वाइड इन्वेंट्री टेबल को अपडेट करती है। इस तालिका में तिथि और आइटम के अनुसार हर दुकान के लिए पंक्तियाँ हैं। कई स्टोर वाले ग्राहकों पर, यह तालिका 500 मिलियन पंक्तियों के आदेश पर बहुत बड़ी हो सकती है।

यह इन्वेंट्री अपडेट प्रक्रिया आम तौर पर दिन में कई बार चलती है क्योंकि स्टोर डेटा दर्ज करते हैं। ये केवल कुछ दुकानों से अपडेट डेटा चलाता है। हालांकि, ग्राहक पिछले 30 दिनों में सभी स्टोर को अपडेट करने, कहने के लिए इसे चला सकते हैं। इस मामले में, प्रक्रिया 10 थ्रेड को फैलाती है और प्रत्येक स्टोर की इन्वेंट्री को एक अलग थ्रेड में अपडेट करती है।

ग्राहक शिकायत कर रहा है कि प्रक्रिया में लंबा समय लग रहा है। मैंने इस प्रक्रिया की रूपरेखा तैयार की है और पाया है कि इस तालिका में एक प्रश्न जो INSERTs का उपयोग करता है, वह मेरी अपेक्षा से अधिक समय ले रहा है। यह INSERT कभी-कभी 30 सेकंड में पूरा होता है।

जब मैं इस तालिका (BEGIN TRAN और ROLLBACK से घिरा) के खिलाफ एक तदर्थ SQL INSERT आदेश चलाता हूं, तो ad-hoc SQL मिलीसेकंड के आदेश पर पूरा होता है।

धीमी गति से निष्पादित क्वेरी नीचे है। यह विचार उन रिकॉर्ड्स को दर्ज करना है जो बाद में नहीं हैं और बाद में उन्हें अद्यतन करने के लिए क्योंकि हम डेटा के विभिन्न बिट्स की गणना करते हैं। इस प्रक्रिया में एक पूर्व कदम ने उन वस्तुओं की पहचान की है जिन्हें अद्यतन करने की आवश्यकता है, कुछ गणनाएं कीं, और परिणाम को tempdb तालिका में अपडेट किया गया Update_Item_Work। यह प्रक्रिया 10 अलग-अलग थ्रेड्स में चल रही है, और प्रत्येक थ्रेड में Update_Item_Work में अपना स्वयं का GUID है।

INSERT INTO Inventory
(
    Inv_Site_Key,
    Inv_Item_Key,
    Inv_Date,
    Inv_BusEnt_ID,
    Inv_End_WtAvg_Cost
)
SELECT DISTINCT
    UpdItemWrk_Site_Key,
    UpdItemWrk_Item_Key,
    UpdItemWrk_Date,
    UpdItemWrk_BusEnt_ID,
    (CASE UpdItemWrk_Set_WtAvg_Cost WHEN 1 THEN UpdItemWrk_WtAvg_Cost ELSE 0 END)
FROM tempdb..Update_Item_Work (NOLOCK)
WHERE UpdItemWrk_GUID = @GUID
AND NOT EXISTS
    -- Only insert for site/item/date combinations that don't exist
    (SELECT *
    FROM Inventory (NOLOCK)
    WHERE Inv_Site_Key = UpdItemWrk_Site_Key
    AND Inv_Item_Key = UpdItemWrk_Item_Key
    AND Inv_Date = UpdItemWrk_Date)

इन्वेंट्री टेबल में 42 कॉलम हैं, जिनमें से अधिकांश विभिन्न इन्वेंट्री समायोजन के लिए मात्रा और गणना को ट्रैक करते हैं। sysinos_db_index_physical_stats का कहना है कि प्रत्येक पंक्ति लगभग 242 बाइट्स है, इसलिए मुझे उम्मीद है कि 33 पंक्तियाँ एक एकल 8 KB पृष्ठ पर फिट होंगी।

तालिका को अद्वितीय बाधा (Inv_Site_Key, Inv_Item_Key, Inv_Date) पर जोड़ा गया है। सभी कुंजियाँ DECIMAL (15,0) हैं, और तारीख SMALLDATETIME है। एक पहचान प्राथमिक कुंजी (नॉनक्लेस्टर्ड) और 4 अन्य सूचकांक हैं। सभी अनुक्रमित और संकुल अवरोध स्पष्ट (FILLFACTOR = 90, PAD_INDEX = ON) के साथ परिभाषित किए गए हैं।

मैंने पृष्ठ विभाजन को गिनने के लिए लॉग फ़ाइल में देखा। मैंने क्लस्टर इंडेक्स पर 1,027 स्प्लिट्स और दूसरे इंडेक्स पर 1,724 स्प्लिट्स को मापा, लेकिन मैंने इस बात को रिकॉर्ड नहीं किया कि क्या अंतराल हुआ है। डेढ़ घंटे बाद, मैंने क्लस्टर इंडेक्स पर 7,035 पेज स्प्लिट्स को मापा।

प्रोफाइलर में कैप्चर की गई क्वेरी योजना इस प्रकार है:

Rows         Executes     StmtText                                                                                                                                             
----         --------     --------                                                                                                                                             
490          1            Sequence                                                                                                                                             
0            1              |--Index Update
0            1              |    |--Collapse
0            1              |         |--Sort
0            1              |              |--Filter
996          1              |                   |--Table Spool                                                                                                                 
996          1              |                        |--Split                                                                                                                  
498          1              |                             |--Assert
0            0              |                                  |--Compute Scalar
498          1              |                                       |--Clustered Index Update(UK_Inventory)
498          1              |                                            |--Compute Scalar
0            0              |                                                 |--Compute Scalar
0            0              |                                                      |--Compute Scalar
498          1              |                                                           |--Compute Scalar
498          1              |                                                                |--Top
498          1              |                                                                     |--Nested Loops
498          1              |                                                                          |--Stream Aggregate
0            0              |                                                                          |    |--Compute Scalar
498          1              |                                                                          |         |--Clustered Index Seek(tempdb..Update_Item_Work)
498          498            |                                                                          |--Clustered Index Seek(Inventory)
0            1              |--Index Update(UX_Inv_Exceptions_Date_Site_Item)
0            1              |    |--Collapse
0            1              |         |--Sort
0            1              |              |--Filter
996          1              |                   |--Table Spool
490          1              |--Index Update(UX_Inv_Date_Site_Item)
490          1                   |--Collapse
980          1                        |--Sort
980          1                             |--Filter
996          1                                  |--Table Spool                                                                                       

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

इस मशीन में लगभग 32 जीबी मेमोरी है। यह SQL Server 2005 मानक संस्करण चला रहा है, हालांकि वे जल्द ही 2008 R2 एंटरप्राइज़ संस्करण में अपग्रेड कर रहे हैं। डिस्क उपयोग के संदर्भ में इन्वेंट्री टेबल कितनी बड़ी है, इस पर मेरे पास संख्या नहीं है, लेकिन यदि आवश्यक हो, तो मैं इसे प्राप्त कर सकता हूं। यह इस प्रणाली की सबसे बड़ी तालिकाओं में से एक है।

मैंने sysinos_io_virtual_file_stats के खिलाफ एक क्वेरी चलाई और औसत लेखन का इंतजार किया tempdb के खिलाफ 1.1 सेकंड से ऊपर होने का इंतजार करता है । जिस डेटाबेस में यह तालिका संग्रहीत की जाती है, उसमें ~ 350 मि। लेकिन वे केवल 6 या उसके बाद अपने सर्वर को फिर से शुरू करते हैं, इसलिए मुझे नहीं पता कि यह जानकारी प्रासंगिक है या नहीं। tempdb 4 अलग-अलग फ़ाइलों में फैला हुआ है उनके पास इन्वेंट्री टेबल रखने वाले डेटाबेस के लिए 3 अलग-अलग फाइलें हैं।

जब एक ही INSERT बहुत तेज होता है तो यह क्वेरी कुछ पंक्तियों के साथ INSERT में इतनी लंबी क्यों होगी?

-- अपडेट करें --

यहां बाइट्स पढ़ने के लिए प्रति ड्राइव विलंबता संख्याएं हैं। जैसा कि आप देख सकते हैं, अस्थायी प्रदर्शन संदिग्ध है। इन्वेंटरी टेबल PDICompany_252_01.mdf, PDICompany_252_01_Second.ndf, या PDICompany_252_01_Third.ndf में या तो है।

ReadLatencyWriteLatencyLatencyAvgBPerRead AvgBPerWriteAvgBPerTransferDriveDB                     physical_name
         42        1112    623       62171       67654          65147R:   tempdb                 R:\Microsoft SQL Server\Tempdb\tempdev1.mdf
         38        1101    615       62122       67626          65109S:   tempdb                 S:\Microsoft SQL Server\Tempdb\tempdev2.ndf
         38        1101    615       62136       67639          65123T:   tempdb                 T:\Microsoft SQL Server\Tempdb\tempdev3.ndf
         38        1101    615       62140       67629          65119U:   tempdb                 U:\Microsoft SQL Server\Tempdb\tempdev4.ndf
         25         341     71       92767       53288          87009X:   PDICompany             X:\Program Files\PDI\Enterprise\Databases\PDICompany_Third.ndf
         26         339     71       90902       52507          85345X:   PDICompany             X:\Program Files\PDI\Enterprise\Databases\PDICompany_Second.ndf
         10         231     90       98544       60191          84618W:   PDICompany_FRx         W:\Program Files\PDI\Enterprise\Databases\PDICompany_FRx.mdf
         61         137     68        9120        9181           9125W:   model                  W:\Microsoft SQL Server\MSSQL.3\MSSQL\Data\modeldev.mdf
         36         113     97        9376        5663           6419V:   model                  V:\Microsoft SQL Server\Logs\modellog.ldf
         22          99     34       92233       52112          86304W:   PDICompany             W:\Program Files\PDI\Enterprise\Databases\PDICompany.mdf
          9          20     10       25188        9120          23538W:   master                 W:\Microsoft SQL Server\MSSQL.3\MSSQL\Data\master.mdf
         20          18     19       53419       10759          40850W:   msdb                   W:\Microsoft SQL Server\MSSQL.3\MSSQL\Data\MSDBData.mdf
         23          18     19      947956       58304         110123V:   PDICompany_FRx         V:\Program Files\PDI\Enterprise\Databases\PDICompany_FRx_1.ldf
         20          17     17      828123       55295         104730V:   PDICompany             V:\Program Files\PDI\Enterprise\Databases\PDICompany.ldf
          5          13     13       12308        4868           5129V:   master                 V:\Microsoft SQL Server\Logs\mastlog.ldf
         11          13     13       22233        7598           8513V:   PDIMaster              V:\Program Files\PDI\Enterprise\Databases\PDIMaster.ldf
         14          11     13       13846        9540          12598W:   PDIMaster              W:\Program Files\PDI\Enterprise\Databases\PDIMaster.mdf
         13          11     11       22350        1107           1110V:   msdb                   V:\Microsoft SQL Server\Logs\MSDBLog.ldf
         17           9      9      745437       11821          23249V:   PDIFoundation          V:\Program Files\PDI\Enterprise\Databases\PDIFoundation.ldf
         34           8     31       29490       33725          30031W:   PDIFoundation          W:\Program Files\PDI\Enterprise\Databases\PDIFoundation.mdf
          5           8      8       61560       61236          61237V:   tempdb                 V:\Microsoft SQL Server\Logs\templog.ldf
         13           6     11        8370       35087          16785W:   SAHost_Company01       W:\Program Files\PDI\Enterprise\Databases\SAHostCompany.mdf
          2           6      5       56235       33667          38911W:   SAHost_Company01       W:\Program Files\PDI\Enterprise\Databases\SAHost_Company_01_log.LDF

टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
पॉल व्हाइट 9

जवाबों:


4

ऐसा लगता है कि आपके क्लस्टर किए गए इंडेक्स पेज स्प्लिट्स दर्दनाक होने जा रहे हैं क्योंकि क्लस्टर इंडेक्स वास्तविक डेटा रखता है और इसके लिए नए पेजों को आवंटित करने की आवश्यकता होती है और डेटा इन पर स्थानांतरित हो जाता है। इससे पृष्ठ लॉकिंग और इस प्रकार अवरुद्ध होने की संभावना है।

यह भी याद रखें कि आपकी क्लस्टर इंडेक्स कुंजी 21 बाइट्स है और इसे बुकमार्क के रूप में आपके सभी द्वितीयक इंडेक्सों में संग्रहीत करना होगा।

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


1

बहु-थ्रेडेड दृष्टिकोण के साथ, मैं उस तालिका में सम्मिलित होने से सावधान हूं, जिसमें से आपको पहले एक कुंजी के अस्तित्व की जांच करनी चाहिए। उस तरह का मुझे कहना है कि उस तालिका के लिए उस PK सूचकांक पर कोई संगामिति मुद्दा है चाहे कितने भी सूत्र हों। उसी कारण से, मुझे इन्वेंट्री टेबल पर NOLOCK संकेत पसंद नहीं है क्योंकि ऐसा लगता है कि एक त्रुटि होगी यदि विभिन्न थ्रेड्स एक ही कुंजी लिखने में सक्षम हैं (क्या विभाजन योजना उस संभावना को हटा देती है?)। मैं उत्सुक हूं कि कई थ्रेड्स के प्रारंभिक परिचय पर स्पीडअप कितना बड़ा था क्योंकि यह किसी बिंदु पर अच्छी तरह से काम किया होगा।

कुछ करने की कोशिश करना क्वेरी को बल्क ऑपरेशन की तरह अधिक पढ़ना और "जहां मौजूद नहीं है" को "एंटी-जॉइन" में बदलना है। (अंततः ऑप्टिमाइज़र इस प्रयास को अनदेखा करना चुन सकता है)। जैसा कि ऊपर बताया गया है कि मैं गंतव्य तालिका पर NOLOCK संकेत निकालता हूँ जब तक कि शायद विभाजन ने थ्रेड्स के बीच कोई महत्वपूर्ण टकराव की गारंटी नहीं दी है।

 INSERT INTO i (...)
 SELECT DISTINCT ...             
   FROM tempdb..Update_Item_Work t (NOLOCK) -- nolock okay on read table
   left join Inventory i -- use without NOLOCK because PK is written inter-thread
     on i.Inv_Site_Key = t.UpdItemWrk_Site_Key
    and i.Inv_Item_Key = t.UpdItemWrk_Item_Key
    and i.Inv_Date = t.UpdItemWrk_Date
  where i.Inv_Site_Key is null   -- where not exist in inventory
    and UpdItemWrk_GUID = @GUID  -- for this thread

आधार के रूप में चलने वाली टाइमिंग, आप एक और संभावना के रूप में मर्ज हिंट ("लेफ्ट जॉइन" -> "लेफ्ट मर्ज जॉइन") के साथ फिर से दौड़ सकते हैं। संभवतः आपको मर्ज संकेत के लिए अस्थायी तालिका (UpdateItemWrk_Site_Key, UpdateItemWrk_Item_Key, UpdateItemWrk_Date) पर एक सूचकांक होना चाहिए।

मुझे नहीं पता कि SQL Server 2008/2012 के नए नॉन-एक्सप्रेस संस्करण हैं या नहीं, इस फॉर्म के बड़े विलय को स्वचालित रूप से समानांतर करने में सक्षम होगा, जिससे आप GUID- आधारित विभाजन को हटा सकते हैं।

सभी आइटम्स के बजाय केवल अलग-अलग आइटम पर होने वाले जॉइन को प्रोत्साहित करने के लिए, "सेलेक्ट ... से ..." सेलेक्ट को "सेलेक्ट * (सेलेक्ट ... से अलग ...)" में कन्वर्ट किया जा सकता है। जुड़ने के साथ जारी है। यह केवल ध्यान देने योग्य अंतर हो सकता है यदि विशिष्ट कई पंक्तियों को फ़िल्टर कर रहा हो। फिर से अनुकूलक इस प्रयास को अनदेखा कर सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.