उत्पादन में कमी लाने वाला प्रवासन शायद ही कभी जटिल होता है। यह आमतौर पर एकलALTER TABLEहोता है एप्लिकेशन के इंतजार के दौरान उसने एक बड़ी मेज पर एक विशेष ताला लगा दिया। शून्य-डाउनटाइम माइग्रेशन एक उपकरण के बजाय एक अनुशासन है: प्रत्येक परिवर्तन को उन चरणों में तोड़ें जो पुराने और नए कोड को एक साथ चलाने के साथ व्यक्तिगत रूप से सुरक्षित हों।
📋 Table of Contents
मूल सिद्धांत: विस्तार और अनुबंध
आप एक ही समय में स्कीमा परिवर्तन और उस कोड को परिनियोजित नहीं कर सकते जिसकी उसे आवश्यकता है। किसी भी रोलिंग परिनियोजन के दौरान, पुराने और नए एप्लिकेशन संस्करण एक साथ चलते हैं। इसलिए प्रत्येक प्रवास दोनों के अनुकूल होना चाहिए।
विस्तार-अनुबंध पैटर्न प्रत्येक परिवर्तन को तीन परिनियोजनों में विभाजित करता है:
- विस्तार करें – नई संरचना जोड़ें. पुराना कोड इसे अनदेखा करता है; नया कोड इसका उपयोग कर सकता है.
- माइग्रेट करें – डेटा बैकफ़िल करें और एप्लिकेशन को नई संरचना पर स्विच करें।
- अनुबंध – जब पुरानी संरचना का कोई संदर्भ न मिले तो उसे हटा दें।
प्रत्येक चरण अलग-अलग तैनात होता है, और प्रत्येक व्यक्तिगत रूप से प्रतिवर्ती होता है। यही बात पूरे अनुक्रम को सुरक्षित बनाती है।
उदाहरण: कॉलम का नाम बदलना
नाम बदलना मामूली लगता है और यह सबसे खतरनाक ऑपरेशनों में से एक है, क्योंकि यह चलते ही पुराने कोड को तोड़ देता है।
-- ❌ Never do this on a live system.
ALTER TABLE users RENAME COLUMN email TO email_address;
-- Every running instance still querying "email" fails immediately.
सुरक्षित अनुक्रम:
-- Step 1 (expand): add the new column. Nullable, no default — instant.
ALTER TABLE users ADD COLUMN email_address TEXT;
-- Step 2: deploy code that WRITES both columns and READS the old one.
-- UPDATE users SET email = $1, email_address = $1 WHERE id = $2
-- Step 3: backfill existing rows in batches (see below).
-- Step 4: deploy code that READS the new column and still writes both.
-- Step 5: deploy code that only uses the new column.
-- Step 6 (contract): drop the old column, once nothing references it.
ALTER TABLE users DROP COLUMN email;
एक नाम बदलने के लिए छह तैनाती तब तक अत्यधिक लगती है जब तक कि पहली बार एक-चरण का नाम बदलने से आउटेज न हो जाए। अधिकांश टीमें अंततः निर्णय लेती हैं कि नाम बदलना उचित नहीं है और वे केवल मूल नाम ही रखते हैं।
कौन से ऑपरेशन लॉक हैं
यह जानना कि कौन से कथन ब्लॉकिंग लॉक लेते हैं, अधिकांश कौशल है। आधुनिक PostgreSQL पर:
| ऑपरेशन | सुरक्षित? | नोट्स |
|---|---|---|
| निरर्थक कॉलम जोड़ें | सुरक्षित | मेटाडेटा-केवल, तत्काल |
| निरंतर डिफ़ॉल्ट के साथ कॉलम जोड़ें | सुरक्षित | आधुनिक संस्करणों में कोई तालिका पुनर्लेखन नहीं |
| अस्थिर डिफ़ॉल्ट के साथ कॉलम जोड़ें | खतरनाक | संपूर्ण तालिका को पुनः लिखता है |
| अनुक्रमणिका जोड़ें | खतरनाक | ब्लॉक लिखता है – CONCURRENTLY |
| का उपयोग करें मौजूदा कॉलम में NOT NULL जोड़ें | खतरनाक | लॉक के अंतर्गत पूर्ण स्कैन – पहले CHECK बाधा का उपयोग करें |
| कॉलम प्रकार बदलें | खतरनाक | आमतौर पर एक पूर्ण पुनर्लेखन |
| विदेशी कुंजी जोड़ें | खतरनाक | सभी पंक्तियों को मान्य करता है – पहले NOT VALID जोड़ें |
| ड्रॉप कॉलम | सुरक्षित | केवल मेटाडेटा, लेकिन पुराने कोड को तोड़ता है |
हमेशा अपने विशिष्ट डेटाबेस संस्करण के विरुद्ध सत्यापित करें। हाल की रिलीज़ों की तुलना में इन ऑपरेशनों के व्यवहार में काफी सुधार हुआ है, और पुराने संस्करणों के लिए लिखी गई सलाह अक्सर अनावश्यक रूप से सतर्क होती है – या दूसरी दिशा में खतरनाक रूप से पुरानी हो जाती है।
अनुक्रमणिका को सुरक्षित रूप से जोड़ना
-- ❌ Blocks all writes to the table for the duration.
CREATE INDEX idx_users_email ON users(email);
-- ✅ Builds without blocking writes.
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
जानने योग्य दो बातेंCONCURRENTLY. यह लेनदेन ब्लॉक के अंदर नहीं चल सकता है, जिसका अर्थ है कि अधिकांश माइग्रेशन फ्रेमवर्क को इसकी अनुमति देने के लिए स्पष्ट कॉन्फ़िगरेशन की आवश्यकता होती है। और यह आंशिक रूप से विफल हो सकता है, जिससे एक अमान्य सूचकांक पीछे रह जाएगा जिसे साफ किया जाना चाहिए।
-- Find invalid indexes left behind by a failed concurrent build
SELECT indexrelid::regclass AS index_name
FROM pg_index
WHERE NOT indisvalid;
-- Drop and retry
DROP INDEX CONCURRENTLY idx_users_email;
लंबे लॉक के बिना नॉट न्यूल जोड़ना
जोड़ रहा हूँNOT NULL लॉक पकड़कर सीधे पूरी टेबल को स्कैन करता है। एक मान्य CHECK बाधा दो गैर-अवरुद्ध चरणों में समान गारंटी प्राप्त करती है।
-- 1. Add the constraint without validating existing rows — instant.
ALTER TABLE users
ADD CONSTRAINT users_email_not_null
CHECK (email IS NOT NULL) NOT VALID;
-- 2. Validate separately. Takes a weaker lock that allows reads and writes.
ALTER TABLE users VALIDATE CONSTRAINT users_email_not_null;
वही NOT VALID / VALIDATE पैटर्न विदेशी कुंजियों पर लागू होता है, और इसी कारण से।
बड़ी तालिकाओं को बैकफ़िल करना
एक एकलUPDATE लाखों पंक्तियों में ताले होते हैं, भारी राइट-फ़ॉरवर्ड लॉग वॉल्यूम उत्पन्न होता है, और प्रतिकृति को रोक सकता है। इसे बैचें.
-- ❌ One statement, one very long transaction, many locked rows.
UPDATE users SET email_address = email;
-- ✅ Batched, resumable, and gentle on replication.
DO $$
DECLARE
batch_size INT := 5000;
affected INT;
BEGIN
LOOP
UPDATE users
SET email_address = email
WHERE id IN (
SELECT id FROM users
WHERE email_address IS NULL AND email IS NOT NULL
ORDER BY id
LIMIT batch_size
FOR UPDATE SKIP LOCKED
);
GET DIAGNOSTICS affected = ROW_COUNT;
EXIT WHEN affected = 0;
COMMIT;
PERFORM pg_sleep(0.1); -- let replicas catch up
END LOOP;
END $$;
FOR UPDATE SKIP LOCKED आपके एप्लिकेशन द्वारा वर्तमान में अपडेट की जा रही पंक्तियों पर बैकफ़िल को अवरुद्ध होने से रोकता है। बैचों के बीच कम नींद ही प्रतिकृति अंतराल को बढ़ने से रोकती है, और यह वह कदम है जिसे लोग छोड़ देते हैं और पछताते हैं।
बहुत बड़ी तालिकाओं के लिए, बैकफ़िल को माइग्रेशन के बजाय एक अलग कार्य के रूप में चलाएं, ताकि उस पर कभी भी तैनाती की प्रतीक्षा न हो।
लॉक टाइमआउट सेट करें
यह एकल सेटिंग अधिकांश माइग्रेशन आउटेज को रोकती है। इसके बिना, एक माइग्रेशन जो लॉक प्राप्त नहीं कर सकता वह अनिश्चित काल तक प्रतीक्षा करता है – और इसके पीछे कतारबद्ध प्रत्येक क्वेरी भी प्रतीक्षा करती है, जो इस प्रकार हैALTER TABLE संपूर्ण एप्लिकेशन को रोक देता है.
-- Fail fast instead of queueing behind a long-running query.
SET lock_timeout = '3s';
SET statement_timeout = '30s';
ALTER TABLE users ADD COLUMN email_address TEXT;
यदि समय समाप्त हो जाए, तो बाद में पुनः प्रयास करें। एक विफल माइग्रेशन जिसे आप दोबारा चला सकते हैं, चरम ट्रैफ़िक के दौरान लॉक की गई तालिका से कहीं बेहतर है।
डेटाबेस के बीच माइग्रेट करना
किसी नए डेटाबेस इंजन या इंस्टेंस पर जाने से बड़े पैमाने पर समान सिद्धांत का उपयोग होता है।
- दोहराएँ – तार्किक प्रतिकृति या पुराने से नए में परिवर्तन-डेटा-कैप्चर सेट करें। इसे पूरी तरह पकड़ने दो।
- दोहरी पढ़ाई – पुराने डेटाबेस से पढ़ें, और परिणामों की तुलना करने के लिए पृष्ठभूमि में नए से पढ़ें। प्रत्येक बेमेल को लॉग करें और आगे बढ़ने से पहले कारणों को ठीक करें।
- दोहरा लेखन – दोनों को लिखें. पुराना आधिकारिक बना हुआ है।
- काट दो – स्विच नए डेटाबेस पर पढ़ता है। दोनों को लिखते रहें.
- सेवामुक्ति– एक बार जब आप आश्वस्त हो जाएं तो पुराने डेटाबेस पर लिखना बंद कर दें, इसे एक सार्थक अवधि के लिए रोलबैक पथ के रूप में रखें।
चरण 2 में तुलना चरण ही इसे सुरक्षित बनाता है। इसे छोड़ने का मतलब कटओवर के बाद डेटा अंतर की खोज करना है, जब वापस रोल करना महंगा होता है।
रोलबैक योजना
प्रत्येक प्रवासन को एक उत्तर की आवश्यकता होती है “क्या होगा यदि यह गलत है?” चलने से पहले.
योगात्मक परिवर्तन तुच्छ रूप से वापस आते हैं — आपके द्वारा अभी जोड़ा गया कॉलम हटाना सुरक्षित है क्योंकि इस पर कुछ भी निर्भर नहीं करता है।
विनाशकारी परिवर्तन वापस नहीं आते। एक बार जब कोई कॉलम हटा दिया जाता है, तो डेटा ख़त्म हो जाता है। यही कारण है कि अनुबंध के चरण अंतिम और विश्वास की अवधि के बाद ही आते हैं।
बैकफ़िल को भी उत्क्रमणीयता की आवश्यकता होती है। यदि आप किसी कॉलम को ओवरराइट करते हैं, तो मूल मानों को तब तक कहीं रखें जब तक आप निश्चित न हो जाएं।
कभी भी ऐसा माइग्रेशन न लिखें जिसका डाउन स्टेप डेटा हटा देता हो। यदि रोलबैक से जानकारी खो जाएगी, तो माइग्रेशन स्वचालित रूप से प्रतिवर्ती नहीं होना चाहिए – पुनर्प्राप्ति पथ को एक जानबूझकर, मैन्युअल निर्णय बनाएं।
उड़ान-पूर्व चेकलिस्ट
- उत्पादन पैमाने पर उत्पादन डेटा की पुनर्स्थापित प्रतिलिपि पर परीक्षण
- जानें कि क्या प्रत्येक कथन एक ब्लॉकिंग लॉक लेता है
- सेट
lock_timeoutऔरstatement_timeout - प्रत्येक बैकफ़िल को बैच करें और इसे परिनियोजन के बाहर चलाएँ
- पुराने और नए एप्लिकेशन कोड की पुष्टि करें, दोनों मध्यवर्ती स्कीमा के साथ काम करते हैं
- दौरान और बाद में प्रतिकृति अंतराल देखें
- एक सत्यापित बैकअप रखें, और जानें कि पुनर्स्थापना में कितना समय लगता है
- कम ट्रैफ़िक के दौरान तब भी तैनात रहें जब आपको कोई लॉक न होने की उम्मीद हो
निष्कर्ष
शून्य-डाउनटाइम माइग्रेशन कुछ विषयों में आता है:प्रत्येक परिवर्तन को अलग-अलग तैनात किए गए विस्तार, माइग्रेट और अनुबंध चरणों में विभाजित करें; जानें कि कौन से कथन ब्लॉकिंग लॉक लेते हैं और उनसे बचने के लिए CONCURRENTLY और NOT VALID का उपयोग करें; बैच रुक-रुक कर बैकफ़िल करता है ताकि प्रतिकृति बनी रहे; और हमेशा एक लॉक टाइमआउट सेट करें ताकि आपके पूरे एप्लिकेशन को इसके पीछे कतारबद्ध करने के बजाय माइग्रेशन तेजी से विफल हो जाए। पहले माइग्रेशन तक अतिरिक्त तैनाती ओवरहेड की तरह महसूस होती है जो अन्यथा आउटेज का कारण बनती।
🔗 Share this article
✍️ Leave a Comment