SQL सर्वर में विशाल डेटा और प्रदर्शन


20

मैंने SQL सर्वर बैकएंड के साथ एक एप्लिकेशन लिखा है जो संग्रह और संग्रह करता है और बहुत बड़ी मात्रा में रिकॉर्ड करता है। मैंने गणना की है कि, चरम पर, रिकॉर्ड की औसत राशि कहीं न कहीं 3-4 बिलियन प्रति दिन (ऑपरेशन के 20 घंटे) में होती है।

मेरा मूल समाधान (इससे पहले कि मैं डेटा की वास्तविक गणना करता हूं) मेरे आवेदन को उसी तालिका में रिकॉर्ड सम्मिलित करना था जो मेरे ग्राहकों द्वारा उद्धृत किया गया हो। यह दुर्घटनाग्रस्त हो गया और काफी तेज़ी से जल गया, जाहिर है, क्योंकि यह उस तालिका को क्वेरी करना असंभव है, जिसमें कई रिकॉर्ड सम्मिलित हैं।

मेरा दूसरा समाधान 2 डेटाबेस का उपयोग करना था, एक आवेदन द्वारा प्राप्त डेटा के लिए और एक ग्राहक-तैयार डेटा के लिए।

मेरे आवेदन को डेटा प्राप्त होगा, इसे ~ 100k रिकॉर्ड के बैचों में मचान और मचान चरण में थोक-सम्मिलित करें। ~ 100k रिकॉर्ड के बाद, मक्खी पर, पहले की तरह ही स्कीमा के साथ एक और स्टेजिंग टेबल बनाएगा, और उस टेबल पर डालना शुरू करेगा। यह एक टेबल के नाम के साथ एक जॉब टेबल में एक रिकॉर्ड बनाएगा जिसमें 100 k रिकॉर्ड होगा और SQL सर्वर साइड पर एक संग्रहीत प्रक्रिया डेटा को स्टेजिंग टेबल (s) से क्लाइंट-रेडी प्रोडक्शन टेबल तक ले जाएगी, और फिर ड्रॉप करेगी तालिका अस्थायी तालिका मेरे आवेदन द्वारा बनाई गई।

दोनों डेटाबेस में एक ही स्कीमा के साथ 5 टेबल का एक ही सेट होता है, जिसमें स्टेजिंग डेटाबेस को छोड़कर जॉब टेबल होता है। मेज़िंग डेटाबेस में कोई अखंडता की कमी, कुंजी, अनुक्रमित आदि नहीं है ... मेज पर जहां रिकॉर्ड का थोक निवास करेगा। नीचे दिखाया गया है, तालिका का नाम है SignalValues_staging। लक्ष्य यह था कि मेरा आवेदन एसक्यूएल सर्वर में डेटा को जल्द से जल्द स्लैम में लाया जाए। मक्खी पर टेबल बनाने के वर्कफ़्लो ताकि वे आसानी से माइग्रेट किए जा सकते हैं बहुत अच्छी तरह से काम करता है।

निम्नलिखित मेरे मंचन डेटाबेस से 5 प्रासंगिक तालिकाएं हैं, साथ ही मेरी नौकरी तालिका:

स्टेजिंग टेबल संग्रहित प्रक्रिया जो मैंने लिखी है, वह सभी स्टैगिंग टेबल से डेटा को ले जाने और इसे उत्पादन में सम्मिलित करने का काम संभालती है। नीचे मेरी संग्रहीत कार्यविधि का हिस्सा है जो मचान तालिकाओं से उत्पादन में सम्मिलित करता है:

-- Signalvalues jobs table.
SELECT *
      ,ROW_NUMBER() OVER (ORDER BY JobId) AS 'RowIndex'
INTO #JobsToProcess
FROM 
(
    SELECT JobId 
           ,ProcessingComplete  
           ,SignalValueStagingTableName AS 'TableName'
           ,(DATEDIFF(SECOND, (SELECT last_user_update
                              FROM sys.dm_db_index_usage_stats
                              WHERE database_id = DB_ID(DB_NAME())
                                AND OBJECT_ID = OBJECT_ID(SignalValueStagingTableName))
                     ,GETUTCDATE())) SecondsSinceLastUpdate
    FROM SignalValueJobs
) cte
WHERE cte.ProcessingComplete = 1
   OR cte.SecondsSinceLastUpdate >= 120

DECLARE @i INT = (SELECT COUNT(*) FROM #JobsToProcess)

DECLARE @jobParam UNIQUEIDENTIFIER
DECLARE @currentTable NVARCHAR(128) 
DECLARE @processingParam BIT
DECLARE @sqlStatement NVARCHAR(2048)
DECLARE @paramDefinitions NVARCHAR(500) = N'@currentJob UNIQUEIDENTIFIER, @processingComplete BIT'
DECLARE @qualifiedTableName NVARCHAR(128)

WHILE @i > 0
BEGIN

    SELECT @jobParam = JobId, @currentTable = TableName, @processingParam = ProcessingComplete
    FROM #JobsToProcess 
    WHERE RowIndex = @i 

    SET @qualifiedTableName = '[Database_Staging].[dbo].['+@currentTable+']'

    SET @sqlStatement = N'

        --Signal values staging table.
        SELECT svs.* INTO #sValues
        FROM '+ @qualifiedTableName +' svs
        INNER JOIN SignalMetaData smd
            ON smd.SignalId = svs.SignalId  


        INSERT INTO SignalValues SELECT * FROM #sValues

        SELECT DISTINCT SignalId INTO #uniqueIdentifiers FROM #sValues

        DELETE c FROM '+ @qualifiedTableName +' c INNER JOIN #uniqueIdentifiers u ON c.SignalId = u.SignalId

        DROP TABLE #sValues
        DROP TABLE #uniqueIdentifiers

        IF NOT EXISTS (SELECT TOP 1 1 FROM '+ @qualifiedTableName +') --table is empty
        BEGIN
            -- processing is completed so drop the table and remvoe the entry
            IF @processingComplete = 1 
            BEGIN 
                DELETE FROM SignalValueJobs WHERE JobId = @currentJob

                IF '''+@currentTable+''' <> ''SignalValues_staging''
                BEGIN
                    DROP TABLE '+ @qualifiedTableName +'
                END
            END
        END 
    '

    EXEC sp_executesql @sqlStatement, @paramDefinitions, @currentJob = @jobParam, @processingComplete = @processingParam;

    SET @i = @i - 1
END

DROP TABLE #JobsToProcess

मैं उपयोग करता हूं sp_executesqlक्योंकि स्टेजिंग टेबल के लिए तालिका के नाम नौकरियों की मेज में रिकॉर्ड से पाठ के रूप में आते हैं।

यह संग्रहित प्रक्रिया इस dba.stackexchange.com पोस्ट से सीखी गई ट्रिक का उपयोग करके हर 2 सेकंड में चलती है ।

मेरे द्वारा मेरे जीवन के लिए जो समस्या का समाधान नहीं किया गया वह वह गति है जिस पर उत्पादन में आवेषण का प्रदर्शन किया जाता है। मेरा आवेदन अस्थायी मेज़िंग टेबल बनाता है और उन्हें रिकॉर्ड के साथ अविश्वसनीय रूप से जल्दी से भर देता है। उत्पादन में सम्मिलित तालिकाओं की मात्रा के साथ नहीं रखा जा सकता है और अंततः हजारों में तालिकाओं का अधिशेष है। केवल जिस तरह से मैं कभी भी आने वाले डेटा के साथ बनाए रखने कर लिया है सभी चाबियाँ, अनुक्रमित, बाधाओं आदि को दूर करने के ... उत्पादन पर है SignalValuesतालिका। मेरे सामने समस्या यह है कि तालिका इतने सारे रिकॉर्ड के साथ समाप्त हो जाती है कि क्वेरी करना असंभव हो जाता है।

मैंने [Timestamp]बिना किसी लाभ के विभाजन के कॉलम के रूप में तालिका का विभाजन करने की कोशिश की है । इंडेक्सिंग का कोई भी रूप आवेषण को इतना धीमा कर देता है कि वे रख नहीं सकते। इसके अलावा, मुझे हजारों विभाजन (एक-एक मिनट? घंटा?) वर्षों पहले ही बनाने होंगे। मैं यह पता नहीं लगा सका कि उन्हें मक्खी पर कैसे बनाया जाए

मैंने तालिका में एक संगणित कॉलम को जोड़कर विभाजन बनाने की कोशिश की TimestampMinuteजिसका मूल्य, पर INSERT, था DATEPART(MINUTE, GETUTCDATE())। अभी भी बहुत धीमी है।

मैंने इस Microsoft लेख के अनुसार इसे एक मेमोरी-ऑप्टिमाइज़ टेबल बनाने की कोशिश की है । शायद मुझे समझ नहीं आ रहा है कि यह कैसे करना है, लेकिन एमओटी ने आवेषण को किसी तरह धीमा कर दिया।

मैंने संग्रहीत कार्यविधि की निष्पादन योजना की जाँच की है और पाया है कि (मुझे लगता है?) सबसे गहन ऑपरेशन है

SELECT svs.* INTO #sValues
FROM '+ @qualifiedTableName +' svs
INNER JOIN SignalMetaData smd
    ON smd.SignalId = svs.SignalId

मेरे लिए यह समझ में नहीं आता है: मैंने दीवार-घड़ी को संग्रहीत प्रक्रिया में जोड़ दिया है जो अन्यथा साबित हुई।

समय-लॉगिंग के संदर्भ में, ऊपर दिया गया वह विशेष विवरण 100k रिकॉर्ड पर ~ 300ms में निष्पादित होता है।

बयान

INSERT INTO SignalValues SELECT * FROM #sValues

100k रिकॉर्ड पर 2500-3000ms में निष्पादित। प्रति रिकॉर्ड प्रभावित तालिका से हटाना, प्रति:

DELETE c FROM '+ @qualifiedTableName +' c INNER JOIN #uniqueIdentifiers u ON c.SignalId = u.SignalId

एक और 300ms लेता है।

मैं इसे और तेज कैसे बना सकता हूं? क्या SQL सर्वर प्रति दिन अरबों के रिकॉर्ड को संभाल सकता है?

यदि यह प्रासंगिक है, तो यह SQL Server 2014 एंटरप्राइज़ x64 है।

हार्डवेयर की समाकृति:

मैं इस प्रश्न के पहले पास में हार्डवेयर को शामिल करना भूल गया। मेरी गलती।

मैं इसे इन बयानों के साथ पेश करूंगा: मुझे पता है कि मैं अपने हार्डवेयर कॉन्फ़िगरेशन के कारण कुछ प्रदर्शन खो रहा हूं। मैंने कई बार कोशिश की है, लेकिन बजट, सी-लेवल, ग्रहों के अलाइनमेंट आदि के कारण ... दुर्भाग्य से बेहतर सेटअप पाने के लिए मैं कुछ नहीं कर सकता। सर्वर एक वर्चुअल मशीन पर चल रहा है और मैं मेमोरी भी नहीं बढ़ा सकता क्योंकि हमारे पास बस और नहीं है।

यहाँ मेरी सिस्टम जानकारी है:

व्यवस्था की सूचना

स्टोरेज VM सर्वर से iSCSI इंटरफ़ेस के माध्यम से NAS बॉक्स में जुड़ा हुआ है (यह प्रदर्शन को नीचा दिखाएगा)। NAS बॉक्स में RAID 10 कॉन्फ़िगरेशन में 4 ड्राइव हैं। वे 4TB WD WD4000FYYZ कताई डिस्क ड्राइव 6GB / s SATA इंटरफ़ेस के साथ हैं। सर्वर में केवल एक डेटा-स्टोर कॉन्फ़िगर किया गया है ताकि tempdb और मेरा डेटाबेस एक ही डेटास्टोर पर हो।

मैक्स डीओपी शून्य है। क्या मुझे इसे निरंतर मूल्य में बदलना चाहिए या सिर्फ SQL सर्वर को संभालने देना चाहिए? मैं RCSI पर पढ़ता हूं: क्या मैं यह मानने में सही हूं कि RCSI से एकमात्र लाभ पंक्ति अद्यतन के साथ आता है? इनमें से किसी भी विशेष रिकॉर्ड के लिए कभी भी अपडेट नहीं होगा, वे INSERTएड और SELECTएड होंगे। क्या आरसीएसआई अभी भी मुझे लाभान्वित करेगा?

मेरा टेम्पर्ड बी 8 एमबी है। नीचे दिए गए उत्तर के आधार पर, मैं पूरी तरह से अस्थायी से बचने के लिए एक नियमित तालिका में #salalues ​​को बदल दिया। प्रदर्शन हालांकि उसी के बारे में था। मैं tempdb के आकार और वृद्धि को बढ़ाने की कोशिश करूंगा, लेकिन यह देखते हुए कि #salalues ​​का आकार हमेशा कम या ज्यादा रहेगा, मैं उतना ही लाभ प्राप्त करने का अनुमान नहीं लगाता।

मैंने एक निष्पादन योजना बनाई है जिसे मैंने नीचे संलग्न किया है। यह निष्पादन योजना एक मंचन की एक पुनरावृत्ति है - 100k रिकॉर्ड। क्वेरी का निष्पादन काफी त्वरित था, लगभग 2 सेकंड, लेकिन ध्यान रखें कि यह SignalValuesटेबल और टेबल पर अनुक्रमित के बिना है SignalValues, लक्ष्य INSERT, इसमें कोई रिकॉर्ड नहीं है।

निष्पादन योजना


3
क्या आपने पहले ही विलंबित स्थायित्व के साथ प्रयोग किया था?
मार्टिन स्मिथ

2
धीमी उत्पादन आवेषण के साथ कौन से अनुक्रमित थे?
पापाराज़ो

अब तक मुझे नहीं लगता कि यह पता लगाने के लिए यहां पर्याप्त डेटा है कि वास्तव में इतना समय क्या खर्च हो रहा है। क्या यह सीपीयू है? क्या यह आईओ है? जब से आप 30k पंक्तियों प्रति सेकंड हो रही है यह मुझे IO की तरह नहीं लगता है। क्या मैं यह सही समझता हूं कि आप अपने संपूर्ण लक्ष्य को पाने के काफी करीब हैं? आपको प्रति सेकंड 50k पंक्तियों की आवश्यकता है, इसलिए हर 2 सेकंड में एक 100k बैच को पर्याप्त होना चाहिए। अभी एक बैच में 3 सेकंड लगते हैं। एक प्रतिनिधि चलाने की वास्तविक निष्पादन योजना पोस्ट करें। कोई भी सुझाव जो सबसे अधिक समय तक चलने वाले ऑपरेशनों पर हमला नहीं करता है, वह मूट है।
यूएसआर

मैंने निष्पादन योजना पोस्ट की है।
ब्रैंडन

जवाबों:


7

मैंने गणना की है कि, चरम पर, रिकॉर्ड की औसत राशि कहीं न कहीं 3-4 बिलियन प्रति दिन (ऑपरेशन के 20 घंटे) में होती है।

आपके स्क्रीनशॉट से, आपको केवल 8 जीबी मेमोरी कुल रैम और 6 जीबी SQL सर्वर को आवंटित की गई है। यह तरीका बहुत कम है जिसे आप हासिल करने की कोशिश कर रहे हैं।

मेरा सुझाव है कि आप मेमोरी को अधिक मूल्य पर अपग्रेड करें - 256GB और साथ ही अपने VM CPU को भी टक्कर दें।

आपको अपने कार्यभार के लिए इस बिंदु पर हार्डवेयर में निवेश करने की आवश्यकता है।

डेटा लोडिंग प्रदर्शन गाइड का भी संदर्भ लें - यह डेटा को कुशलतापूर्वक लोड करने के स्मार्ट तरीकों का वर्णन करता है।

मेरा टेम्पर्ड बी 8 एमबी है।

आपके संपादन के आधार पर .. आपके पास एक समझदार tempdb होना चाहिए - अधिमानतः कई tempdb डेटा फाइलें TF 1117 और 1118 सक्षम उदाहरण के साथ समान रूप से चौड़ी हैं।

मैं आपको एक पेशेवर स्वास्थ्य जांच कराने और वहां से शुरू करने का सुझाव दूंगा।

बहुत अधिक सिफारिश की जाती है

  1. अपने सर्वर युक्ति को टक्कर दें।

  2. एक पेशेवर * व्यक्ति अपने डेटाबेस सर्वर उदाहरण का स्वास्थ्य परीक्षण करें और सिफारिशों का पालन करें।

  3. एक बार। और बी। किया जाता है, फिर अपने आप को क्वेरी ट्यूनिंग और अन्य अनुकूलन में विसर्जित कर दें जैसे कि प्रतीक्षा आँकड़े, प्रश्न योजना आदि।

नोट: मैं एक हूँ पेशेवर एसक्यूएल सर्वर विशेषज्ञ पर hackhands.com - एक Pluralsight कंपनी है, लेकिन किसी भी तरह में मदद के लिए मुझे किराया करने के लिए आप का सुझाव दे। मैं आपको केवल अपने संपादन के आधार पर पेशेवर मदद लेने का सुझाव दे रहा हूं।

HTH।


मैं एक साथ एक प्रस्ताव रखने की कोशिश कर रहा हूं (पढ़ें: भीख मांगना) इसके लिए अधिक हार्डवेयर। यहाँ और आपके उत्तर को ध्यान में रखते हुए, SQL सर्वर कॉन्फ़िगरेशन या क्वेरी ऑप्टिमाइज़ेशन दृष्टिकोण से कुछ और नहीं है जो आप इसे और तेज़ बनाने का सुझाव देंगे?
ब्रैंडन

1

बड़े डेटा के साथ ऐसी समस्याओं के लिए सामान्य सलाह, जब दीवार का सामना करना पड़ता है और कुछ भी काम नहीं करता है:

एक अंडा के बारे में 5 मिनट पकाया जा रहा है। 10 अंडे एक ही समय में पकाया जाएगा यदि पर्याप्त बिजली और पानी।

या, दूसरे शब्दों में:

सबसे पहले, हार्डवेयर को देखें; दूसरा, प्रक्रिया तर्क (डेटा रीमॉडेलिंग) को अलग करें और इसे समानांतर में करें।

गतिशील और स्वचालित, प्रति तालिका गणना और प्रति तालिका आकार में कस्टम वर्टिकल विभाजन बनाना काफी संभव है; अगर मेरे पास Quarter_1_2017, Quarter_2_2017, Quarter_3_2017, Quarter_4_2017, Quarter_1_2018 ... और मुझे नहीं पता कि मेरे रिकॉर्ड कहाँ हैं और मेरे पास कितने विभाजन हैं, तो एक ही समय में सभी कस्टम विभाजनों के विरुद्ध एक ही क्वेरी (s) चलाएँ, अलग-अलग सत्र और असेंबली। मेरे तर्क के लिए आगे संसाधित होने का परिणाम।


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

-1

मैं निम्नलिखित जांच / अनुकूलन करूंगा:

  1. उत्पादन डेटाबेस के डेटा और लॉग फ़ाइल दोनों को सम्मिलित करें कार्रवाई के दौरान विकसित नहीं होता है (यदि आवश्यक हो तो इसे पूर्व-विकसित करें)

  2. प्रयोग नहीं करें

    select * into [dest table] from [source table];

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

  3. डायनामिक एसक्यूएल का उपयोग करने के बजाय, मैं हार्ड-कोडित टेबल नामों का उपयोग कुछ कोडिंग लॉजिक के साथ करना चाहूंगा कि किस टेबल को संचालित करना है।

  4. मैं मेमोरी, सीपीयू और डिस्क I / O के प्रदर्शन की निगरानी भी करूंगा कि क्या बड़े वर्कलोड के दौरान संसाधन भुखमरी हैं या नहीं।

  5. चूंकि आपने उल्लेख किया है कि आप उत्पादन पक्ष पर अनुक्रमित छोड़ने से प्रविष्टि को संभाल सकते हैं, मैं जांच करूंगा कि क्या कई पृष्ठ विभाजन हो रहे हैं, यदि हां, तो मैं अनुक्रमितों के भराव को कम कर दूंगा और इससे पहले कि मैं ड्रॉप करने पर विचार करूँ अनुक्रमित करता है।

गुड लक और अपने सवाल से प्यार है।


जवाब के लिए धन्यवाद। मैंने डेटाबेस का आकार 1 जीबी पर सेट किया था और 1 जीबी बढ़ने का अनुमान था कि विकास कार्यों में कुछ समय लगेगा, जिसने शुरुआत में गति के साथ मदद की। मैं आज प्री-ग्रोथ को लागू करने की कोशिश करूंगा। मैंने एक नियमित टेबल के रूप में [डेस्ट] टेबल को लागू किया, लेकिन बहुत अधिक प्रदर्शन हासिल नहीं किया। मेरे पास पिछले कुछ दिनों से ज्यादा समय नहीं है, लेकिन मैं आज दूसरे लोगों से मिलने की कोशिश करूंगा।
ब्रैंडन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.