আমি একটি এসকিউএল সার্ভার ব্যাকএন্ডের সাথে একটি অ্যাপ্লিকেশন লিখেছি যা সংগ্রহ করে এবং সঞ্চয় করে এবং অত্যন্ত পরিমাণে রেকর্ড। আমি গণনা করেছি যে, শীর্ষে, রেকর্ডের গড় পরিমাণ কোথাও কোথাও প্রতিদিন 3-4 ডলার (অপারেশন 20 ঘন্টা) এর এভিনিউতে থাকে।
আমার আসল সমাধানটি (আমি ডেটার প্রকৃত গণনাটি করার আগে) আমার অ্যাপ্লিকেশনটিকে আমার ক্লায়েন্টদের জিজ্ঞাসা করা একই টেবিলে রেকর্ড সন্নিবেশ করানো ছিল। এটি ক্র্যাশ হয়ে গেছে এবং মোটামুটি দ্রুত পুড়ে গেছে, স্পষ্টতই, কারণ এমন একটি টেবিলের জিজ্ঞাসা করা অসম্ভব যে এতে অনেকগুলি রেকর্ড .োকানো আছে।
আমার দ্বিতীয় সমাধানটি ছিল 2 টি ডাটাবেস ব্যবহার করা, একটি অ্যাপ্লিকেশন দ্বারা প্রাপ্ত ডেটার জন্য এবং একটি ক্লায়েন্ট-প্রস্তুত ডেটার জন্য।
আমার অ্যাপ্লিকেশনটি ডেটা গ্রহণ করবে, এটি ~ 100k রেকর্ডের ব্যাচগুলিতে ছেঁকে ফেলবে এবং মঞ্চ টেবিলে বাল্ক-সন্নিবেশ করত। K 100k রেকর্ড করার পরে অ্যাপ্লিকেশনটি ফ্লাইতে আগের মতো একই স্কিমার সাথে অন্য একটি স্টেজিং টেবিল তৈরি করবে এবং সেই টেবিলটিতে সন্নিবেশ শুরু করবে। এটি 100 টাকার রেকর্ডযুক্ত টেবিলের নাম সহ একটি কাজের টেবিলে একটি রেকর্ড তৈরি করবে এবং এসকিউএল সার্ভারের সঞ্চিত পদ্ধতি স্টেজিং টেবিল (গুলি) থেকে ক্লায়েন্ট-প্রস্তুত উত্পাদনের টেবিলে স্থানান্তরিত করবে এবং তারপরে ড্রপটি ফেলে দেবে টেবিল অস্থায়ী টেবিল আমার অ্যাপ্লিকেশন দ্বারা নির্মিত।
উভয় ডাটাবেসে একই স্কিমার সাথে 5 টি টেবিলের সমান সেট রয়েছে, স্টেজিং ডেটাবেস বাদে চাকরি সারণী রয়েছে। স্টেজিং ডাটাবেসের কোনও টেবিলে কোনও সততার বাধা, কী, সূচিপত্র ইত্যাদি নেই ... যেখানে প্রচুর রেকর্ড থাকবে। নীচে দেখানো হয়েছে, টেবিলের নাম SignalValues_staging
। লক্ষ্যটি ছিল আমার অ্যাপ্লিকেশনটি যত তাড়াতাড়ি সম্ভব এসকিউএল সার্ভারে ডেটা স্ল্যাম করা। ফ্লাইতে টেবিলগুলি তৈরির কার্যপ্রবাহ যাতে তারা সহজেই স্থানান্তরিত হতে পারে তা বেশ ভাল কাজ করে।
নীচে আমার স্টেজিং ডাটাবেস থেকে 5 টি সম্পর্কিত টেবিল, এবং আমার কাজের সারণি:
আমি লিখিত সঞ্চিত পদ্ধতিটি মঞ্চের সমস্ত টেবিল থেকে ডেটা সরানো এবং এটিকে উত্পাদনে handোকানো পরিচালনা করে। নীচে আমার সঞ্চিত পদ্ধতির অংশ যা মঞ্চ টেবিলগুলি থেকে উত্পাদনে প্রবেশ করে:
-- 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())
। এখনও খুব ধীর।
এই মাইক্রোসফ্ট নিবন্ধ অনুযায়ী আমি এটিকে একটি মেমরি-অপ্টিমাইজড টেবিল তৈরি করার চেষ্টা করেছি । এটি আমি কীভাবে করব তা আমি বুঝতে পারি না তবে এমওটি সন্নিবেশগুলিকে একরকম আস্তে করে তোলে।
আমি সঞ্চিত পদ্ধতির এক্সিকিউশন প্ল্যানটি পরীক্ষা করে দেখেছি যে (আমার মনে হয়?) সর্বাধিক নিবিড় অপারেশন
SELECT svs.* INTO #sValues
FROM '+ @qualifiedTableName +' svs
INNER JOIN SignalMetaData smd
ON smd.SignalId = svs.SignalId
আমার কাছে এটির কোনও অর্থ নেই: আমি সঞ্চিত পদ্ধতিতে ওয়াল-ক্লক লগিং যুক্ত করেছি যা অন্যথায় প্রমাণিত হয়েছে।
টাইম-লগিংয়ের ক্ষেত্রে, উপরের সেই নির্দিষ্ট বিবৃতিটি 100 কে রেকর্ডে ms 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 লাগে।
আমি কীভাবে এটি দ্রুত করতে পারি? এসকিউএল সার্ভার প্রতিদিন বিলিয়ন বিলিয়ন রেকর্ডে পরিচালনা করতে পারে?
যদি এটি প্রাসঙ্গিক হয় তবে এটি এসকিউএল সার্ভার 2014 এন্টারপ্রাইজ x64।
হার্ডওয়্যার কনফিগারেশন:
আমি এই প্রশ্নের প্রথম পাসে হার্ডওয়্যার অন্তর্ভুক্ত করতে ভুলে গেছি। আমার খারাপ।
আমি এই বিবৃতিগুলির সাথে এটির উপস্থাপনা করব: আমি জানি আমার হার্ডওয়্যার কনফিগারেশনের কারণে আমি কিছু কর্মক্ষমতা হারাচ্ছি। আমি অনেকবার চেষ্টা করেছি কিন্তু বাজেটের কারণে, সি-লেভেল, গ্রহগুলির সারিবদ্ধকরণ ইত্যাদি ... দুর্ভাগ্যক্রমে আরও ভাল সেটআপ পাওয়ার জন্য আমি কিছুই করতে পারি না। সার্ভারটি ভার্চুয়াল মেশিনে চলছে এবং আমি মেমরিটি এমনকি বাড়িয়ে তুলতে পারি না কারণ আমাদের কাছে আর কিছুই নেই।
আমার সিস্টেমের তথ্য এখানে:
স্টোরেজটি কোনও এনএএস বাক্সে আইএসসিএসআই ইন্টারফেসের মাধ্যমে ভিএম সার্ভারের সাথে সংযুক্ত করা হয়েছে (এটি কর্মক্ষমতা হ্রাস করবে)। এনএএস বক্সে একটি রেড 10 কনফিগারেশনে 4 ড্রাইভ রয়েছে। তারা 6 জিবি / এস এসটিএ ইন্টারফেসের সাথে 4TB ডাব্লুডি ডাব্লুডি 4000 ফাইওয়াইজেড স্পিনিং ডিস্ক ড্রাইভ। সার্ভারে কেবলমাত্র একটি ডেটা-স্টোর কনফিগার করা হয়েছে তাই টেম্পডিবি এবং আমার ডাটাবেস একই ডাটাস্টোরে রয়েছে are
সর্বোচ্চ ডিওপি শূন্য। আমি কি এটি একটি ধ্রুবক মানতে পরিবর্তন করব বা এসকিউএল সার্ভারকে এটি পরিচালনা করতে পারি? আমি আরসিএসআই-তে পড়েছি: আরসিএসআইয়ের একমাত্র উপকারটি সারি আপডেট নিয়ে আসে তা ধরে নিয়ে আমি কি সঠিক? এই নির্দিষ্ট রেকর্ডের INSERT
কোনওটিতেই কখনও আপডেট হবে না, সেগুলি সম্পাদনা এবং SELECT
সম্পাদনা করা হবে। আরসিএসআই কি এখনও আমার উপকার করবে?
আমার টেম্পডিবি 8 এমবি। জ্যাও থেকে নীচের উত্তরের ভিত্তিতে, আমি সম্পূর্ণরূপে টেম্পডিবি এড়ানোর জন্য # টি ভ্যালুগুলিকে একটি নিয়মিত টেবিলে পরিবর্তন করেছি। পারফরম্যান্স যদিও প্রায় একই ছিল। আমি টেম্পটিডিবির আকার এবং বৃদ্ধি বাড়ানোর চেষ্টা করব, তবে # এসভ্যালুগুলির আকার কমবেশি সর্বদা একই আকারের হবে বলে আমি বেশি লাভের প্রত্যাশা করি না।
আমি একটি বাস্তবায়ন পরিকল্পনা গ্রহণ করেছি যা আমি নীচে সংযুক্ত করেছি। এই এক্সিকিউশন প্ল্যানটি স্টেজিং টেবিলের একটি পুনরাবৃত্তি - 100 কে রেকর্ড। ক্যোয়ারির সম্পাদন মোটামুটি দ্রুত, প্রায় 2 সেকেন্ড ছিল, তবে মনে রাখবেন যে এটি SignalValues
টেবিল এবং টেবিলের সূচী ছাড়াই SignalValues
, এর লক্ষ্যবস্তু এর INSERT
কোনও রেকর্ড নেই।