در دنیای مهندسی پایگاه داده، چالشهای کمی به اندازه تکامل اسکیما یک برنامه مونولیت عظیم و قدیمی ترسناک هستند. رویکرد سنتی حذف جدول، افزودن ستون یا تغییر محدودیت، اغلب منجر به قفل شدن پایگاه داده به مدت دقیقهها یا حتی ساعتها میشود و مجبور به ایجاد پنجره نگهداری میکند که ذینفعان کسبوکار با تردید آن را تأیید میکنند. علاوه بر این، بسیاری از سازمانها تحت محدودیت عدم آمادگی برای میکروسرویسها یا شاردینگ پایگاه داده عمل میکنند.
این راهنما استراتژی مستحکمی را برای پیادهسازی مهاجرات اسکیما بدون قطعی برای سیستمهای مونولیت قدیمی تشریح میکند. با بهرهگیری از راهاندازیهای مرحلهای، الگوهای نوشتن دوتایی و عملیات DDL غیرمسدودکننده، میتوانید مدل داده خود را بدون شاردینگ و بدون وقفه در ترافیک زنده، بهطور ایمن تکامل دهید.
استراتژی مهاجرت سهفازی
فلسفه اصلی مهاجرات بدون قطعی، جداسازی استقرار کد از استقرار تغییرات اسکیما است. برای هر تغییر قابل توجه در اسکیما—مانند افزودن یک ستون غیرقابل خالی، تغییر نام یک ستون یا تغییر یک ایندکس—معمولاً از یک رویکرد سهفازی پیروی میکنیم: گسترش، مهاجرت و انقباض.
در فاز گسترش، ما اسکیما را برای پذیرش وضعیت آینده تغییر میدهیم در حالی که سازگاری با کد فعلی برنامه را حفظ میکنیم. فاز مهاجرت شامل پر کردن هرگونه نیازمندی داده جدید بدون مسدود کردن نوشتنها است. در نهایت، فاز انقباض الگوهای قدیمی را پس از اطمینان از اینکه تمام ترافیک از ساختار جدید استفاده میکند، پاکسازی میکند.
افزودن ستونهای غیرقابل خالی
افزودن ستونی که نمیتواند خالی باشد، یک تله کلاسیک است. اگر صرفاً دستور ALTER TABLE users ADD COLUMN status VARCHAR(20) NOT NULL; را اجرا کنید، بیشتر پایگاههای داده تلاش میکنند تا هر سطر موجود را اسکن کرده و آنها را با مقدار پیشفرض پر کنند که ممکن است جدول را قفل کرده و باعث خطاهای زمانبندی شود.
بهجای آن، ابتدا اجازه دهید ستون خالی باشد، سطرهای موجود را بهصورت ناهمگام بهروزرسانی کنید و سپس محدودیت را اعمال نمایید. در اینجا نحوه رویکرد مرحلهای به زبان SQL آمده است:
-- فاز ۱: گسترش
-- ستون را بهصورت قابل خالی اضافه کنید تا از قفل شدن فوری یا پر کردن مجدد جلوگیری شود
ALTER TABLE users ADD COLUMN status VARCHAR(20) NULL;
-- کد برنامه را بهگونهای بهروزرسانی کنید که به هر دو ستون 'old_status' و 'new_status' بنویسد
-- یا صرفاً 'status' را همانطور که برنامه سطرهای رکورد را بهطور طبیعی بهروزرسانی میکند، پر کنید.
در منطق برنامه شما، هنگامی که یک رکورد کاربر بهروزرسانی میشود، اطمینان حاصل کنید که به ستون جدید نیز نوشته میشود. برای دادههای قدیمی که لمس نشدهاند، میتوانید یک وظیفه پسزمینه برای پر کردن مجدد داده اجرا کنید:
-- فاز ۲: مهاجرت (وظیفه پسزمینه)
-- مقادیر خالی موجود را بهصورت قطعات بهروزرسانی کنید تا از تراکنشهای طولانی جلوگیری شود
UPDATE users
SET status = 'active'
WHERE status IS NULL
AND id > 10000
AND id <= 20000; -- تکرار در حلقهها با دستهبندی
پس از اینکه وظیفه پسزمینه تأیید کرد که ستون بهطور کامل پر شده است، میتوانید بهطور ایمن محدودیت را اعمال کنید:
-- فاز ۳: انقباض
ALTER TABLE users ALTER COLUMN status SET NOT NULL;
-- اگر ستون نوشتن دوتایی داشتید، اکنون ستون قدیمی را حذف کنید
ALTER TABLE users DROP COLUMN old_status;
تغییر نام جداول و ستونها
تغییر نام اغلب بهعنوان یک عملیات پرخطر در نظر گرفته میشود زیرا بلافاصله کوئریهای موجود را خراب میکند. برای کاهش این ریسک، از نمایها (Views) بهعنوان لایه انتزاعی استفاده کنید. هنگام تغییر نام یک ستون یا جدول، نام جدیدی ایجاد کنید در حالی که نام قدیمی را دستنخورده نگه دارید. اسکیما پایگاه داده را بهگونهای بهروزرسانی کنید که نمای را به جدول فیزیکی جدید اشاره دهد یا از نام ستون جدید در کوئریهای جدید استفاده کنید.
برای مثال، اگر در حال تغییر نام user_id به account_id هستید:
-- گام ۱: افزودن ستون جدید
ALTER TABLE orders ADD COLUMN account_id BIGINT;
کدبیس خود را بهگونهای بهروزرسانی کنید که به account_id بخواند و بنویسد. پس از تأیید استقرار کد، میتوانید دادهها را پر کنید و در نهایت ستون قدیمی را حذف نمایید. این اطمینان را ایجاد میکند که اگر کد جدید شکست بخورد، پایگاه داده در وضعیتی باقی میماند که با کد قدیمی سازگار باشد.
استفاده از ویژگیهای خاص پایگاه داده
سیستمهای پایگاه داده مدرن ویژگیهای خاصی را برای کمک به این مهاجرات ارائه میدهند. پستگرس (PostgreSQL)، بهعنوان مثال، عملیات CONCURRENTLY را برای ایندکسها و محدودیتهای یکتا اجازه میدهد که نیاز به قفل کامل جدول ندارند. همیشه از اینها در صورت موجود بودن بهره ببرید.
-- در پستگرس، این اجازه میدهد ایندکس بدون مسدود کردن نوشتنها ایجاد شود
CREATE INDEX CONCURRENTLY idx_orders_account ON orders(account_id);
همیشه اسکریپتهای مهاجرت خود را در محیط آزمایشی که حجم دادههای تولید را شبیهسازی میکند، آزمایش کنید. تفاوت بین یک مهاجرت ۱۰ دقیقهای روی یک مجموعه داده کوچک و یک مهاجرت ۶ ساعته روی یک مجموعه داده به مقیاس ترابایت بسیار قابل توجه است.
نتیجهگیری
مهاجرت سیستمهای مونولیت قدیمی نیازی به گزینه اتمی شاردینگ یا معماریهای پیچیده میکروسرویس ندارد. با پایبندی به الگوی گسترش-مهاجرت-انقباض و توجه به مکانیزمهای قفل، مهندسان پایگاه داده میتوانند تکامل پیوسته را هدایت کنند. این رویکرد ریسک را کاهش میدهد، در دسترس بودن بالا را حفظ میکند و به تیم شما اجازه میدهد تا با همان سرعتی که کد برنامه تغییر میکند، روی مدل داده نیز تکرار و بهبود داشته باشد. به یاد داشته باشید، هدف نه تنها تغییر اسکیما، بلکه انجام آن بدون هرگز منتظر گذاشتن مشتریان شماست.