একটি এসকিউএল টেবিল থেকে কয়েক মিলিয়ন সারি মুছুন


9

আমাকে 221+ মিলিয়ন সারি সারণী থেকে 16+ মিলিয়ন রেকর্ড মুছতে হবে এবং এটি অত্যন্ত ধীরে ধীরে চলছে।

আপনি নীচের কোডটি দ্রুত করার জন্য পরামর্শগুলি ভাগ করে নিলে আমি প্রশংসা করি:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

DECLARE @BATCHSIZE INT,
        @ITERATION INT,
        @TOTALROWS INT,
        @MSG VARCHAR(500);
SET DEADLOCK_PRIORITY LOW;
SET @BATCHSIZE = 4500;
SET @ITERATION = 0;
SET @TOTALROWS = 0;

BEGIN TRY
    BEGIN TRANSACTION;

    WHILE @BATCHSIZE > 0
        BEGIN
            DELETE TOP (@BATCHSIZE) FROM MySourceTable
            OUTPUT DELETED.*
            INTO MyBackupTable
            WHERE NOT EXISTS (
                                 SELECT NULL AS Empty
                                 FROM   dbo.vendor AS v
                                 WHERE  VendorId = v.Id
                             );

            SET @BATCHSIZE = @@ROWCOUNT;
            SET @ITERATION = @ITERATION + 1;
            SET @TOTALROWS = @TOTALROWS + @BATCHSIZE;
            SET @MSG = CAST(GETDATE() AS VARCHAR) + ' Iteration: ' + CAST(@ITERATION AS VARCHAR) + ' Total deletes:' + CAST(@TOTALROWS AS VARCHAR) + ' Next Batch size:' + CAST(@BATCHSIZE AS VARCHAR);             
            PRINT @MSG;
            COMMIT TRANSACTION;
            CHECKPOINT;
        END;
END TRY
BEGIN CATCH
    IF @@ERROR <> 0
       AND @@TRANCOUNT > 0
        BEGIN
            PRINT 'There is an error occured.  The database update failed.';
            ROLLBACK TRANSACTION;
        END;
END CATCH;
GO

এক্সিকিউশন প্ল্যান (2 পুনরাবৃত্তির জন্য সীমাবদ্ধ)

এখানে চিত্র বর্ণনা লিখুন

VendorIdএটি পিকে এবং নন-ক্লাস্টার্ড , যেখানে ক্লাস্টার্ড সূচকটি এই স্ক্রিপ্টটির দ্বারা ব্যবহৃত হয় না। এখানে 5 টি অন্যান্য অ-অনন্য, ক্লাস্টারযুক্ত সূচক নেই।

টাস্কটি হ'ল "বিক্রেতাদের অপসারণ যা অন্য টেবিলে বিদ্যমান নেই" এবং সেগুলি অন্য টেবিলে ব্যাক আপ করে। আমার কাছে 3 টেবিল রয়েছে vendors, SpecialVendors, SpecialVendorBackups,। টেবিলের SpecialVendorsঅস্তিত্ব নেই যা সরিয়ে দেওয়ার চেষ্টা করা হচ্ছে Vendors, এবং আমি যা করছি তা ভুল হয়েছে এবং মুছে ফেলা রেকর্ডগুলির একটি ব্যাকআপ রাখার চেষ্টা করছি এবং আমি এক বা দুই সপ্তাহের মধ্যে তাদের ফিরিয়ে দিতে হবে।


আমি এই ক্যোয়ারীটি অপ্টিমাইজ করার কাজ করব এবং বাম
পাপারাজ্জো

উত্তর:


8

সম্পাদন পরিকল্পনাটি দেখায় যে এটি কোনও ক্রমবিহীন সূচক থেকে সারিগুলি পড়ছে যাতে প্রতিটি বাহ্যিক সারিটি মূল্যায়নের জন্য পড়ার চেষ্টা করে NOT EXISTS

এখানে চিত্র বর্ণনা লিখুন

আপনি টেবিলের 7.2% মুছছেন। 4,500 এর 3,556 ব্যাচে 16,000,000 সারি

ধরে নিই যে যোগ্য হওয়া সারিগুলি ঘটনাচক্রে পুরো সূচিগুলিতে বিতরণ করা হয় তার অর্থ এটি প্রতি 13.8 টি সারিতে প্রায় 1 টি সারি মুছে ফেলবে।

সুতরাং পুনরাবৃত্তি 1 62,156 টি সারি পড়বে এবং মুছে ফেলার জন্য 4,500 খুঁজে পাওয়ার আগে অনেকগুলি সূচী অনুসন্ধান করবে perform

পুনরাবৃত্তি 2 57,656 (62,156 - 4,500) সারিগুলি পড়বে যা অবশ্যই কোনও সমবর্তী আপডেটগুলি উপেক্ষা করার যোগ্যতা অর্জন করবে না (যেমন তারা ইতিমধ্যে প্রক্রিয়াজাত হয়েছে) এবং তারপরে মুছে ফেলার জন্য আরও 62,156 সারি পাওয়া যাবে।

পুনরাবৃত্তি 3 (2 * 57,656) + 62,156 টি সারি পড়বে এবং অবশেষে পুনরাবৃত্ত হওয়া অবধি ৩, will৫6 পড়বে (৩, 57৫৫ * 57,656) + 62,156 সারি এবং বহু সন্ধান করবে।

সুতরাং সমস্ত ব্যাচ জুড়ে সঞ্চালিত সূচীর সংখ্যা সংখ্যা SUM(1, 2, ..., 3554, 3555) * 57,656 + (3556 * 62156)

যা ((3555 * 3556 / 2) * 57656) + (3556 * 62156)- বা364,652,494,976

আমি আপনাকে পরামর্শ দেব যে আপনি প্রথমে একটি টেম্প টেবিলের মোছার জন্য সারিগুলি কার্যকর করুন material

INSERT INTO #MyTempTable
SELECT MySourceTable.PK,
       1 + ( ROW_NUMBER() OVER (ORDER BY MySourceTable.PK) / 4500 ) AS BatchNumber
FROM   MySourceTable
WHERE  NOT EXISTS (SELECT *
                   FROM   dbo.vendor AS v
                   WHERE  VendorId = v.Id) 

এবং পরিবর্তন DELETEমুছে ফেলতে WHERE PK IN (SELECT PK FROM #MyTempTable WHERE BatchNumber = @BatchNumber)আপনি এখনও একটি অন্তর্ভুক্ত করা প্রয়োজন হতে পারে NOT EXISTSমধ্যে DELETEআপডেটের জন্য কোয়েরি নিজেই পূরণ করার জন্য যেহেতু টেম্প টেবিল জনবহুল হলেও এই অনেক আরও দক্ষ যেমন শুধুমাত্র 4,500 ব্যাচ প্রতি কামনা সঞ্চালন করতে হবে হওয়া উচিত।


আপনি যখন "একটি টেম্প টেবিলের মধ্যে সারিগুলি মুছে ফেলতে প্রথমে সারণী তৈরি করেন" বলছেন আপনি যখন তাদের সমস্ত কলামের সাথে সমস্ত রেকর্ড টেম্প টেবিলের মধ্যে রাখার পরামর্শ দিচ্ছেন? নাকি শুধু PKকলাম? (আমি বিশ্বাস করি আপনি আমাকে সেগুলি পুরোপুরি
টেম্পল

@ কিলারার - কেবল মূল কলাম (গুলি)
মার্টিন স্মিথ

আপনি দ্রুত পর্যালোচনা করতে পারেন এই যদি আমি কি আপনি সঠিকভাবে না বললেন, দয়া করে পেতে পারি?
cilerler

@ কিলারার - DELETE TOP (@BATCHSIZE) FROM MySourceTableকেবলমাত্র DELETE FROM MySourceTable টেম্প টেবিলটিও সূচী করা উচিত CREATE TABLE #MyTempTable ( Id BIGINT, BatchNumber BIGINT, PRIMARY KEY(BatchNumber, Id) );এবং VendorIdস্পষ্টভাবে নিজেরাই পিকে? আপনার কাছে 221 মিলিয়ন বিভিন্ন বিক্রেতা রয়েছে?
মার্টিন স্মিথ

ধন্যবাদ মার্টিন, সন্ধ্যা 6 টার পরে এটি পরীক্ষা করবে। এবং আপনার উত্তরটি হ'ল, অবশ্যই এই টেবিলটিতে
কেবলমাত্র পিকেই

4

এক্সিকিউশন প্ল্যান পরামর্শ দেয় যে প্রতিটি ক্রমাগত লুপ আগের লুপের চেয়ে বেশি কাজ করবে। ধরে নিলে মুছে ফেলা সারিগুলি টেবিল জুড়ে সমানভাবে বিতরণ করা হয়েছে প্রথম লুপটি মোছার জন্য 4500 সারি খুঁজতে প্রায় 4500 * 221000000/16000000 = 62156 সারি স্ক্যান করতে হবে। এটি vendorটেবিলের বিপরীতে একই সংখ্যক ক্লাস্টারড সূচক চাইবে । যাইহোক, দ্বিতীয় লুপটি একইভাবে 62156 - 4500 = 57656 সারিগুলি পড়তে হবে যা আপনি প্রথম বার মুছলেন না। আমরা দ্বিতীয় লুপটি 120000 সারি স্ক্যান করে 120000 সারণীর MySourceTableবিপরীতে করতে চাইব vendor। লুপ প্রতি কাজের প্রয়োজনীয় পরিমাণ একটি রৈখিক হারে বৃদ্ধি পায়। একটি অনুমান হিসাবে আমরা বলতে পারি যে গড় লুপটির জন্য 102516868 সারিগুলি পড়তে হবে MySourceTableএবং 102516868 এর বিপরীতে চেষ্টা করতে হবেvendorটেবিল। 4500 ব্যাচের আকারের 16 মিলিয়ন সারি মুছতে আপনার কোডটি 16000000/4500 = 3556 লুপগুলি করা দরকার, সুতরাং আপনার কোডটি সম্পূর্ণ করার জন্য কাজের পরিমাণ প্রায় 364.5 বিলিয়ন সারি থেকে পড়ে MySourceTableএবং 364.5 বিলিয়ন সূচি সন্ধান করে।

একটি ছোট সমস্যা হ'ল আপনি @BATCHSIZEকোনও শীর্ষ প্রকাশে স্থানীয় পরিবর্তনশীলটি কোনও RECOMPILEবা অন্য কোনও ইঙ্গিত ছাড়াই ব্যবহার করেন । পরিকল্পনা তৈরি করার সময় ক্যোয়ারী অপ্টিমাইজার সেই স্থানীয় ভেরিয়েবলের মান জানবে না। এটি ধরে নেওয়া হবে যে এটি 100 এর সমান reality বাস্তবে আপনি 100 এর পরিবর্তে 4500 সারি মুছে ফেলছেন এবং এই তাত্পর্য হওয়ার কারণে আপনি সম্ভবত কম দক্ষ পরিকল্পনা নিয়ে শেষ করতে পারেন। কোনও টেবিলে whenোকানোর সময় স্বল্প কার্ডিনালিটি অনুমানও পারফরম্যান্স হিট করতে পারে। এসকিউএল সার্ভার 4500 সারিগুলির বিপরীতে 100 টি সারি সন্নিবেশ করা প্রয়োজন মনে করে সন্নিবেশগুলি করতে আলাদা অভ্যন্তরীণ API বেছে নিতে পারে।

একটি বিকল্প হ'ল আপনি একটি অস্থায়ী সারণীতে মুছে ফেলতে চান এমন সারিগুলির প্রাথমিক কী / ক্লাস্টারযুক্ত কীগুলি সন্নিবেশ করানো। আপনার কী কলামগুলির আকারের উপর নির্ভর করে এটি সহজেই টেম্পডিবিতে ফিট করতে পারে। আপনি সেই ক্ষেত্রে ন্যূনতম লগিং পেতে পারেন যার অর্থ লেনদেনের লগ আপ হবে না। এর একটি পুনরুদ্ধার মডেল সহ আপনি কোনও ডাটাবেসের বিরুদ্ধে ন্যূনতম লগিং পেতে পারেন SIMPLE। প্রয়োজনীয়তা সম্পর্কে আরও তথ্যের জন্য লিঙ্কটি দেখুন।

যদি এটি কোনও বিকল্প না হয় তবে আপনার কোডটি পরিবর্তন করা উচিত যাতে আপনি ক্লাস্টারড ইনডেক্সের সুবিধা নিতে পারেন MySourceTable। মূল জিনিসটি হ'ল আপনার কোডটি লিখুন যাতে আপনি লুপ প্রতি প্রায় একই পরিমাণ কাজ করেন। আপনি প্রতিবার প্রথম থেকেই টেবিলটি স্ক্যান করার পরিবর্তে সূচকের সুবিধা নিয়ে তা করতে পারেন। আমি একটি ব্লগ পোস্ট লিখেছিলাম যা লুপিংয়ের বিভিন্ন পদ্ধতির উপর পড়ে। এই পোস্টের উদাহরণগুলি মুছে ফেলার পরিবর্তে একটি সারণিতে সন্নিবেশ করায় তবে আপনার কোডটি মানিয়ে নিতে সক্ষম হওয়া উচিত।

নীচের উদাহরণ কোডে আমি ধরে নিলাম যে আপনার প্রাথমিক কী এবং ক্লাস্টারযুক্ত কী MySourceTable। আমি এই কোডটি বেশ দ্রুত লিখেছি এবং এটি পরীক্ষা করতে সক্ষম হচ্ছি না:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

DECLARE @BATCHSIZE INT,
        @ITERATION INT,
        @TOTALROWS INT,
        @MSG VARCHAR(500)
        @STARTID BIGINT,
        @NEXTID BIGINT;
SET DEADLOCK_PRIORITY LOW;
SET @BATCHSIZE = 4500;
SET @ITERATION = 0;
SET @TOTALROWS = 0;

SELECT @STARTID = ID
FROM MySourceTable
ORDER BY ID
OFFSET 0 ROWS
FETCH FIRST 1 ROW ONLY;

SELECT @NEXTID = ID
FROM MySourceTable
WHERE ID >= @STARTID
ORDER BY ID
OFFSET (60000) ROWS
FETCH FIRST 1 ROW ONLY;

BEGIN TRY
    BEGIN TRANSACTION;

    WHILE @STARTID IS NOT NULL
        BEGIN
            WITH MySourceTable_DELCTE AS (
                SELECT TOP (60000) *
                FROM MySourceTable
                WHERE ID >= @STARTID
                ORDER BY ID
            )           
            DELETE FROM MySourceTable_DELCTE
            OUTPUT DELETED.*
            INTO MyBackupTable
            WHERE NOT EXISTS (
                                 SELECT NULL AS Empty
                                 FROM   dbo.vendor AS v
                                 WHERE  VendorId = v.Id
                             );

            SET @BATCHSIZE = @@ROWCOUNT;
            SET @ITERATION = @ITERATION + 1;
            SET @TOTALROWS = @TOTALROWS + @BATCHSIZE;
            SET @MSG = CAST(GETDATE() AS VARCHAR) + ' Iteration: ' + CAST(@ITERATION AS VARCHAR) + ' Total deletes:' + CAST(@TOTALROWS AS VARCHAR) + ' Next Batch size:' + CAST(@BATCHSIZE AS VARCHAR);             
            PRINT @MSG;
            COMMIT TRANSACTION;

            CHECKPOINT;

            SET @STARTID = @NEXTID;
            SET @NEXTID = NULL;

            SELECT @NEXTID = ID
            FROM MySourceTable
            WHERE ID >= @STARTID
            ORDER BY ID
            OFFSET (60000) ROWS
            FETCH FIRST 1 ROW ONLY;

        END;
END TRY
BEGIN CATCH
    IF @@ERROR <> 0
       AND @@TRANCOUNT > 0
        BEGIN
            PRINT 'There is an error occured.  The database update failed.';
            ROLLBACK TRANSACTION;
        END;
END CATCH;
GO

মূল অংশটি এখানে:

WITH MySourceTable_DELCTE AS (
    SELECT TOP (60000) *
    FROM MySourceTable
    WHERE ID >= @STARTID
    ORDER BY ID
)   

প্রতিটি লুপ এ থেকে 60000 সারি পড়বে MySourceTable। এর ফলে প্রতি লেনদেনের গড় 4500 সারি আকার মোছা এবং প্রতি লেনদেনের সর্বাধিক 60000 সারি আকার মোছার ফলাফল হওয়া উচিত। আপনি যদি একটি ছোট ব্যাচের আকারের সাথে আরও রক্ষণশীল হতে চান তবে এটি খুব ভাল। @STARTIDপ্রতিটি লুপ পর পরিবর্তনশীল অগ্রগতি আপনি উৎস টেবিল থেকে একাধিকবার একই সারিতে পড়া এড়াতে পারেন।


বিস্তারিত তথ্যের জন্য আপনাকে ধন্যবাদ। আমি 4500 সীমাটি সারণী লক না করে সেট করেছি set যদি আমি ভুল না হয়ে থাকি তবে এসকিউএলটির একটি হার্ড সীমা রয়েছে যা মুছে ফেলা গণনাটি 5000 এর উপরে চলে গেলে পুরো টেবিলটিকে লক করে দেয় And এবং যেহেতু এটি দীর্ঘ প্রক্রিয়া হবে আমি দীর্ঘ সময় ধরে সেই টেবিলটি লক করার চেষ্টা করতে পারি না। আমি যদি 60000 থেকে 4500 সেট করি তবে আপনি কি মনে করেন যে আমিও একই পারফরম্যান্স পাব?
cilerler

@ কাইলার আপনি যদি লক বাড়ানোর বিষয়ে উদ্বিগ্ন থাকেন তবে আপনি এটি টেবিল পর্যায়ে অক্ষম করতে পারেন। ৪৫০০ ব্যাচের আকার ব্যবহারের ক্ষেত্রে কোনও ভুল নেই key মূলটি হ'ল প্রতিটি লুপ মোটামুটি একই পরিমাণ কাজ করবে do
জো ওবিশ

গতির পার্থক্যের কারণে আমাকে অন্য উত্তর গ্রহণ করতে হবে। আমি আপনার সমাধানটি এবং @ মার্টিন-স্মিথের সমাধানটি পরীক্ষা করেছি এবং তার সংস্করণটি 10 ​​মিনিটের পরীক্ষার জন্য আরও ডেটা - 2% পাচ্ছে। আপনার সমাধানগুলি আমার চেয়ে অনেক ভাল এবং আমি আপনার সময়ের জন্য সত্যই প্রশংসা করি ... -
cilerler

2

দুটি চিন্তা মনে বসন্ত:

বিলম্ব সম্ভবত ডেটা ভলিউমের সাথে সূচকের কারণে। সূচকগুলি বাদ দেওয়ার, মুছে ফেলার এবং সূচিগুলি পুনরায় তৈরি করার চেষ্টা করুন।

অথবা ..

আপনি যে সারিগুলি অস্থায়ী টেবিলের মধ্যে রাখতে চান তা অনুলিপি করা, 16 মিলিয়ন সারি দিয়ে টেবিলটি ফেলে দেওয়া এবং অস্থায়ী টেবিলটির নতুন নামকরণ (বা উত্স সারণীর নতুন উদাহরণে অনুলিপি করা) দ্রুততর হতে পারে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.