تغييرات قاعدة البيانات الإضافية: كيفية الإضافة دون تعطيل الإنتاج
لديك تطبيق يعمل مع آلاف المستخدمين النشطين. يحتاج فريقك إلى إضافة حقل رقم الهاتف إلى ملف المستخدم. يبدو التغيير صغيرًا، لكن مجرد التفكير في تشغيل ALTER TABLE على قاعدة بيانات إنتاجية يجعل الجميع متوترين. ماذا لو أدى الترحيل إلى قفل الجدول؟ ماذا لو تعطل كود التطبيق القديم لأنه لا يتوقع وجود العمود الجديد؟
هذا الموقف يتكرر في كل فريق هندسي تقريبًا يدير قاعدة البيانات الخاصة به. الخبر السار هو أن فئة واحدة من تغييرات المخطط (schema) آمنة بشكل ملحوظ، حتى تحت ضغط الإنتاج. فهم هذه الفئة يغير طريقة تخطيطك للنشر، وكيفية التنسيق مع فريقك، ومقدار المخاطرة التي تتحملها عند تطوير قاعدة البيانات الخاصة بك.
ما الذي يجعل التغيير إضافيًا
التغيير الإضافي هو أي تعديل يضيف شيئًا جديدًا فقط إلى مخطط قاعدة البيانات دون تغيير أو إزالة أي شيء موجود بالفعل. القائمة واضحة:
- إضافة عمود جديد إلى جدول موجود
- إنشاء جدول جديد
- إضافة فهرس جديد
- إضافة قيد (constraint) لا يقيد البيانات الموجودة
الخاصية الأساسية هي أنك تقوم بتوسيع المخطط. أنت لا تعيد تسمية الأعمدة، أو تغير أنواع البيانات، أو تدمج الجداول، أو تحذف أي شيء. الأجزاء القديمة من المخطط تبقى كما هي تمامًا.
لماذا التغييرات الإضافية آمنة
الأمان يأتي من حقيقة بسيطة: كود التطبيق القديم لا يعتمد على الشيء الجديد الذي أضفته للتو. عندما تضيف عمود phone إلى جدول users، يستمر كود التطبيق الحالي في قراءة وكتابة name و email و password تمامًا كما كان يفعل دائمًا. ليس لديه أي فكرة بوجود عمود جديد. لا يحاول قراءته، ولا يحاول الكتابة إليه. لا شيء يتعطل.
هذا ليس صحيحًا لأنواع أخرى من التغييرات. إذا قمت بإعادة تسمية عمود من phone إلى phone_number، سيحاول كود التطبيق القديم قراءة phone وسيفشل لأن هذا العمود لم يعد موجودًا. إذا قمت بتغيير نوع عمود من VARCHAR إلى INT، قد يكتب الكود القديم قيمة نصية لم يعد بإمكان قاعدة البيانات قبولها. إذا قمت بدمج جدولين، فإن الاستعلامات القديمة التي تشير إلى بنية الجدول الأصلية ستنكسر.
تتجنب التغييرات الإضافية كل هذه المشاكل لأنها لا تلمس أي شيء يعتمد عليه الكود القديم.
القاعدة الحرجة الوحيدة
هناك شرط واحد يجعل التغييرات الإضافية آمنة: يجب أن يكون العمود الجديد قابلاً للقيم الفارغة (nullable) أو أن يكون له قيمة افتراضية.
إذا أضفت عمودًا بـ NOT NULL وبدون قيمة افتراضية، فإن كل صف موجود في الجدول سيفشل في تلبية القيد فورًا. سترفض قاعدة البيانات الترحيل. في أسوأ الحالات، يعمل الترحيل جزئيًا، وينجح لبعض الصفوف، ثم يفشل، تاركًا لك بيانات غير متسقة يصعب تنظيفها.
النمط القياسي يبدو كالتالي:
ALTER TABLE users ADD COLUMN phone VARCHAR(20) NULL;
إذا كنت تعلم أن العمود سيكون مطلوبًا في النهاية، أضف قيمة افتراضية أولاً:
ALTER TABLE users ADD COLUMN phone VARCHAR(20) NOT NULL DEFAULT '';
مع قيمة افتراضية، يحصل كل صف موجود على سلسلة فارغة. لا يزال بإمكان كود التطبيق القديم قراءة هذا العمود دون أخطاء. لاحقًا، بعد تحديث جميع نسخ التطبيق للتعامل مع العمود الجديد بشكل صحيح، يمكنك إزالة القيمة الافتراضية أو إضافة قيد NOT NULL في ترحيل منفصل.
تشغيل التغييرات الإضافية خلال ساعات الإنتاج
واحدة من أكبر المزايا العملية للتغييرات الإضافية هي أنه يمكنك تشغيلها بينما يكون الإنتاج مشغولاً. في معظم قواعد البيانات الحديثة، ALTER TABLE ADD COLUMN مع عمود قابل للقيم الفارغة هي عملية خفيفة لا تقفل الجدول لفترة طويلة.
هناك استثناءات. الإصدارات الأقدم من MySQL يمكنها قفل الجدول أثناء ALTER TABLE. إضافة عمود في موضع محدد باستخدام AFTER يمكن أن تسبب قفلًا أكثر من إضافته في النهاية. لكن بشكل عام، إضافة عمود قابل للقيم الفارغة هي واحدة من أكثر العمليات أمانًا التي يمكنك تنفيذها على قاعدة بيانات حية.
هذا يعني أنك لا تحتاج إلى نافذة صيانة. لا تحتاج إلى الانتظار لساعات حركة المرور المنخفضة. يمكنك تشغيل الترحيل خلال النهار، والتحقق من أنه يعمل، والانتقال إلى الخطوة التالية من النشر.
كيف يتيح ذلك النشر التدريجي
التغييرات الإضافية تفتح استراتيجية نشر يصعب تحقيقها مع أنواع أخرى من تغييرات المخطط: التحديثات المتدرجة (rolling updates) دون تنسيق.
تخيل أن لديك عشر نسخ من التطبيق تعمل خلف موازن تحميل. تريد نشر إصدار جديد يقرأ ويكتب عمود phone. إليك كيفية عمل العملية:
- قم بتشغيل الترحيل لإضافة عمود
phone. المخطط الآن لديه العمود الجديد، لكن جميع النسخ العشر لا تزال تعمل بالكود القديم الذي يتجاهله. - قم بتحديث نسخة واحدة إلى الكود الجديد. تبدأ تلك النسخة في قراءة وكتابة عمود
phone. النسخ التسع الأخرى تستمر في العمل بشكل طبيعي لأنها لا تلمس العمود الجديد أبدًا. - قم بتحديث النسخ المتبقية تدريجيًا واحدة تلو الأخرى. في أي نقطة خلال هذه العملية، تتعايش النسخ القديمة والجديدة دون تعارضات.
هذا ممكن فقط لأن تغيير المخطط إضافي. إذا كان التغيير يتطلب حذف عمود أو إعادة تسميته، فإن النسخ القديمة ستنكسر في اللحظة التي يتم فيها تشغيل الترحيل. ستكون مجبرًا على تحديث جميع النسخ مرة واحدة، مما يزيد المخاطر ويتطلب تنسيقًا دقيقًا.
متى تتجاوز التغييرات الإضافية
التغييرات الإضافية آمنة، لكنها ليست كافية دائمًا. في النهاية، ستحتاج إلى إزالة الأعمدة غير المستخدمة، أو تغيير أنواع البيانات لإصلاح مشاكل الأداء، أو إعادة هيكلة الجداول لدعم ميزات جديدة. تلك التغييرات تحمل مخاطر أكبر وتتطلب استراتيجيات مختلفة.
التسلسل العملي هو: ابدأ بالتغييرات الإضافية لإدخال أعمدة وجداول جديدة، واترك كود التطبيق يلحق بالركب، ثم خطط للتغييرات الأكثر تدخلاً في ترحيلات منفصلة. يجب أن يقوم كل ترحيل بشيء واحد فقط. لا تخلط تغييرًا إضافيًا مع تغيير مدمر في نفس سكريبت الترحيل.
قائمة مراجعة سريعة للتغييرات الإضافية
قبل تشغيل تغيير إضافي في الإنتاج، راجع هذه النقاط:
- هل العمود الجديد قابل للقيم الفارغة، أو هل له قيمة افتراضية؟
- هل يضيف الترحيل أشياء فقط، ولا يعدل أو يزيل مخططًا موجودًا؟
- هل اختبرت الترحيل على نسخة من بيانات الإنتاج؟
- هل لديك خطة تراجع؟ (للتغييرات الإضافية، التراجع عادة ما يكون
ALTER TABLE DROP COLUMN، لكن تحقق من أنه يعمل.) - هل يمكن لكود التطبيق القديم الاستمرار في العمل دون تغييرات بعد الترحيل؟
إذا كان بإمكانك الإجابة بنعم على كل هذه الأسئلة، فأنت جاهز لتشغيل الترحيل بثقة.
الخلاصة العملية
التغييرات الإضافية هي الفئة الأكثر أمانًا من تعديلات مخطط قاعدة البيانات لأنها توسع المخطط دون كسر أي شيء يعتمد عليه الكود القديم. استخدم الأعمدة القابلة للقيم الفارغة أو القيم الافتراضية، وقم بتشغيلها خلال ساعات العمل العادية، وانشر نسخ تطبيقك تدريجيًا. هذا النهج يزيل التوتر بين تطوير قاعدة البيانات الخاصة بك والحفاظ على استقرار الإنتاج. ابدأ كل تغيير في المخطط بسؤال: هل يمكنني جعل هذا إضافيًا؟ إذا كانت الإجابة بنعم، فافعل ذلك أولاً. احفظ التغييرات الأكثر خطورة لوقت لاحق، عندما يكون المخطط الجديد قيد الاستخدام بالفعل ويتم تقاعد الكود القديم.