تشغيل ترحيلات قاعدة البيانات في الإنتاج دون قلق

خط أنابيب النشر أخضر. تمت مراجعة تغييرات الكود والموافقة عليها. بيئة الاختبار تبدو جيدة. ثم تأتي اللحظة التي يخشاها كل مهندس: تشغيل الترحيل (migration) على قاعدة بيانات الإنتاج.

هنا يكمن الخطر الحقيقي. تغيير في المخطط (schema) يعمل بشكل مثالي على حاسوبك المحمول قد يقفل جدول إنتاج لدقائق. ترحيل بيانات يعمل بسلاسة مع ألف صف قد يستغرق ساعات مع مليون صف. وعندما تسوء الأمور، يبدأ التطبيق في إرجاع أخطاء لمستخدمين حقيقيين.

المشكلة ليست تقنية فقط. إنها تتعلق أيضًا بالتوقيت والتنسيق ومعرفة متى تتوقف.

متى تشغيل الترحيل

على عكس نشر التطبيقات، الذي يمكنك غالبًا القيام به في أي ساعة، فإن ترحيلات قاعدة البيانات لها عواقب مباشرة على أداء الاستعلامات وتوفر البيانات. الوقت الأكثر أمانًا لتشغيلها هو عندما تكون حركة المرور منخفضة. تسمي العديد من الفرق هذا نافذة التوقف (downtime window)، لكن هذا الاسم مضلل. نافذة التوقف لا تعني أن التطبيق يجب أن يكون غير متصل. إنها تعني أنك وافقت على فترة يمكنك فيها إجراء تغييرات قد تسبب اضطرابًا، وأنك استعدت لهذا الاحتمال.

السؤال الحقيقي هو: هل يمكن لترحيلك أن يعمل بينما لا يزال التطبيق يخدم حركة المرور؟ إذا صممت ترحيلاتك بعناية، فغالبًا ما تكون الإجابة نعم. لكن ذلك يعتمد على فهم ما تفعله قاعدة البيانات عند تشغيل أوامر معينة.

مشكلة الأقفال (Locking)

المصدر الأكثر شيوعًا للمشاكل أثناء ترحيلات الإنتاج هو الأقفال. عند تشغيل أمر مثل ALTER TABLE، تقوم معظم قواعد البيانات بقفل الجدول لمنع التغييرات الأخرى أثناء تنفيذ العملية. إذا استغرق هذا الأمر وقتًا طويلاً، فإن كل استعلام من تطبيقك يلمس ذلك الجدول إما ينتظر أو يفشل. يبدأ المستخدمون في رؤية صفحات بطيئة أو رسائل خطأ.

توفر بعض قواعد البيانات طرقًا لتقليل الأقفال. يدعم PostgreSQL CREATE INDEX CONCURRENTLY، الذي ينشئ فهرسًا دون حظر عمليات الكتابة. لدى MySQL خيارات مماثلة لبعض العمليات. لكن ليس كل تغيير يمكن القيام به بدون قفل. إضافة عمود بقيمة افتراضية، تغيير نوع عمود، أو إزالة عمود غالبًا ما يتطلب قفلًا حصريًا.

المفتاح هو معرفة ما تدعمه قاعدة البيانات قبل كتابة الترحيل. تحقق من الوثائق الخاصة بإصدار قاعدة البيانات الخاصة بك. اختبر الترحيل على نسخة من بيانات الإنتاج الخاصة بك، وليس فقط عينة صغيرة. قس المدة التي سيستمر فيها القفل في ظل ظروف واقعية.

على سبيل المثال، إليك نمط آمن لإضافة عمود بقيمة افتراضية في PostgreSQL، لتجنب قفل حصري طويل:

-- الخطوة 1: إضافة العمود كقابل للقيم الخالية (سريع، لا إعادة كتابة افتراضية)
ALTER TABLE users ADD COLUMN display_name text;

-- الخطوة 2: ملء العمود الخلفي في دفعات صغيرة
UPDATE users SET display_name = username WHERE display_name IS NULL LIMIT 1000;
-- كرر حتى لا يتبقى صفوف

-- الخطوة 3: تعيين NOT NULL (سريع، لا إعادة كتابة بيانات)
ALTER TABLE users ALTER COLUMN display_name SET NOT NULL;

تقسيم الترحيلات الكبيرة إلى خطوات صغيرة

خطأ شائع هو محاولة فعل الكثير في ترحيل واحد. على سبيل المثال، إضافة عمود جديد، ملؤه ببيانات من عمود آخر، ثم حذف العمود القديم كلها في خطوة واحدة. هذا يخلق عملية طويلة الأمد تحمل أقفالًا طوال المدة.

النهج الأكثر أمانًا هو تقسيم العمل إلى عدة ترحيلات أصغر:

  1. أضف العمود الجديد بدون قيمة افتراضية.
  2. قم بتشغيل وظيفة خلفية (background job) لملء العمود الجديد في دفعات.
  3. تحقق من اكتمال البيانات وصحتها.
  4. احذف العمود القديم في ترحيل منفصل.

يمكن التحقق من كل خطوة قبل الانتقال إلى التالية. إذا حدث خطأ ما في الخطوة الثانية، لم تخسر شيئًا. يمكنك إصلاح البيانات وإعادة المحاولة. هذا النهج يستغرق وقتًا أطول، لكنه أقل خطورة بكثير.

بناء فحوصات أمان في خط الأنابيب

لا ينبغي لخط الأنابيب الخاص بك تشغيل الترحيلات بشكل أعمى. يحتاج إلى التحقق من الحالة الحالية لقاعدة البيانات قبل إجراء التغييرات. قبل بدء الترحيل، يمكن لخط الأنابيب التحقق من وجود استعلامات طويلة الأمد. إذا كانت هناك استعلامات تعمل لأكثر من بضع ثوانٍ، يجب أن ينتظر الترحيل. قد تكون تلك الاستعلامات تحمل أقفالًا قد تمنع الترحيل، أو قد يمنعها الترحيل.

يوضح مخطط التدفق التالي عملية فحص الأمان الموصوفة أعلاه:

flowchart TD A[بدء الترحيل] --> B{حركة المرور منخفضة؟} B -- نعم --> C[تشغيل الترحيل مع تحليل الأقفال] B -- لا --> D[انتظر وأعد الفحص] D --> B C --> E{تم تحرير الأقفال؟} E -- نعم --> F{زمن استجابة الاستعلام طبيعي؟} E -- لا --> G[أوقف وأبلغ] F -- نعم --> H[تأكيد النجاح] F -- لا --> G G --> I[تراجع إذا لزم الأمر]

أثناء الترحيل، يجب على خط الأنابيب مراقبة:

  • مدة التنفيذ: كم من الوقت تستغرق كل عبارة
  • وقت انتظار القفل: هل تنتظر استعلامات أخرى على الأقفال
  • معدل الأخطاء: أي أخطاء من قاعدة البيانات أو التطبيق

إذا تجاوز أي من هذه المقاييس حدًا معينًا، يجب على خط الأنابيب إيقاف الترحيل وإخطار الفريق. هذا ليس عن الحذر الزائد. إنه عن وجود آلية واضحة لمنع مشكلة صغيرة من أن تصبح حادثة إنتاج.

ماذا يحدث بعد اكتمال الترحيل

اكتمل الترحيل بدون أخطاء. خط الأنابيب يظهر أخضر. لكن العمل لم ينته بعد.

قد لا يزال تطبيقك لديه اتصالات قاعدة بيانات قديمة مفتوحة. تقوم مجمعات الاتصالات (connection pools) بتخزين الاتصالات مؤقتًا، وقد تحمل تلك الاتصالات المخزنة مؤقتًا مراجع للمخطط القديم. بعض ORMs تخزن مؤقتًا خطط الاستعلام التي لم تعد تطابق المخطط الجديد. هذه المشكلات لا تظهر دائمًا فورًا. يمكن أن تسبب أخطاء خفية بعد دقائق أو ساعات.

تقوم العديد من الفرق بإعادة تشغيل التطبيق بعد الترحيل، أو على الأقل تصريف الاتصالات القديمة والسماح بإنشاء اتصالات جديدة. ينتظر آخرون بضع دقائق أثناء مراقبة السجلات والمقاييس قبل إعلان نجاح الترحيل. النهج الدقيق يعتمد على مجموعة التطبيقات الخاصة بك وكيفية عمل تجميع الاتصالات.

قائمة تحقق عملية لترحيلات الإنتاج

قبل تشغيل ترحيل في الإنتاج، اتبع قائمة التحقق هذه:

  • اختبر الترحيل على نسخة من بيانات الإنتاج، وليس فقط قاعدة بيانات تطوير
  • قس المدة التي تستغرقها كل عبارة وما الأقفال التي تحصل عليها
  • تحقق من أن إصدار قاعدة البيانات يدعم البدائل الخالية من الأقفال لعملية
  • تحقق من وجود استعلامات طويلة الأمد قبل بدء الترحيل
  • أعد مراقبة وقت انتظار القفل ومعدل الأخطاء أثناء الترحيل
  • حدد شرط إيقاف واضح: أي مقياس أو خطأ يؤدي إلى التوقف
  • أعد خطة تراجع: كيفية عكس الترحيل إذا حدث خطأ ما
  • أبلغ الفريق قبل وبعد تشغيل الترحيل
  • خطط لإعادة تشغيل التطبيق أو تحديث الاتصالات بعد الترحيل

الخلاصة

تشغيل ترحيل قاعدة بيانات في الإنتاج ليس عن تجنب المخاطر. إنه عن فهم المخاطر، وتقسيمها إلى أجزاء يمكن التحكم فيها، والحصول على إشارات واضحة لمتى تتوقف. أفضل الترحيلات هي تلك التي لا يلاحظها أحد لأنها عملت بسلاسة. ثاني أفضل الترحيلات هي تلك التي تم إيقافها مبكرًا قبل أن تسبب ضررًا حقيقيًا. أسوأها هي تلك التي اكتملت لكنها كسرت التطبيق بطرق استغرقت ساعات لاكتشافها. ابنِ خط الأنابيب الخاص بك ليهدف إلى الفئة الأولى، لكن كن دائمًا مستعدًا للثانية.