मैंने 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
, इसमें कोई रिकॉर्ड नहीं है।