في وقت مبكر من عمر منتج، تغيير مخطط قاعدة بيانات عادة أمر بسيط: أخذ التطبيق دون اتصال لفترة صيانة قصيرة، تشغيل الترحيل، وإعادة تشغيل التطبيق. مع نمو منتج — مزيد من المستخدمين، ومزيد من الإيرادات المرتبطة بوقت التشغيل — يتوقف هذا النهج عن كونه مقبولاً، وتحتاج الفرق لطريقة لتغيير بنية قاعدة بيانات حية بينما يستمر التطبيق في القراءة والكتابة إليها دون انقطاع. هذا أصعب مما يبدو، ليس لأن SQL لتغيير جدول صعب الكتابة، بل لأن تغيير مخطط وتطبيق قيد التشغيل يعملان، لفترة ما، ضد نفس قاعدة البيانات في آن واحد.
تدور تقنية الترحيل بلا توقف أساساً حول إدارة تلك الفترة الانتقالية عمداً: إجراء تغييرات بتسلسل وشكل بحيث، في كل نقطة زمنية واحدة أثناء الترحيل، يمكن لكل من الكود القديم والجديد للتطبيق العمل بشكل صحيح ضد مخطط قاعدة البيانات كما يبدو في تلك اللحظة بالضبط. هذا يعني عادة أن تغيير مخطط منطقي واحد يُقسَّم إلى خطوات أصغر متعددة قابلة للنشر بشكل مستقل، بدلاً من تطبيقه كعملية ذرية واحدة.
لماذا يكسر تغيير المخطط المباشر تطبيقاً قيد التشغيل
لنأخذ أبسط تغيير مخطط ممكن: إعادة تسمية عمود. إذا تم هذا كعملية ذرية واحدة — إعادة تسمية العمود، وعندها فقط نشر كود تطبيق جديد يتوقع الاسم الجديد — توجد نافذة لا مفر منها بين سريان تغيير المخطط واكتمال نشر كود التطبيق الجديد عبر كل نسخة قيد التشغيل، خلالها يعمل الكود القديم، الذي لا يزال يتوقع الاسم القديم، ضد مخطط لم يعد يحتويه. في نظام له أكثر من نسخة قيد التشغيل، تعني عمليات النشر المتدرجة أن الكود القديم والجديد يعملان، بالتصميم، في آن واحد لفترة ما.
نمط التوسيع-الانكماش
التقنية الأساسية لتغيير المخطط بلا توقف تُسمى عادة التوسيع-الانكماش، وتعمل بتقسيم تغيير مخطط واحد إلى ثلاث مراحل متمايزة تُنشَر بشكل مستقل، كل واحدة آمنة بذاتها. في مرحلة التوسيع، يُضاف عنصر المخطط الجديد جنباً إلى جنب مع القديم دون إزالة أي شيء — يُضاف عمود جديد بينما يبقى العمود القديم، أو يُنشأ جدول جديد بينما يستمر الكتابة إلى القديم — بحيث يمكن لكل من كود التطبيق الحالي قيد التشغيل والكود الجديد المستقبلي التعايش ضد هذا المخطط الموسَّع دون أن يكسر أي منهما.
بمجرد نشر مرحلة التوسيع واستقرارها، يُحدَّث كود التطبيق للكتابة إلى عنصري المخطط القديم والجديد في آن واحد، وغالباً بعد عملية إعادة تعبئة لملء العنصر الجديد للبيانات الموجودة التي تسبق التغيير، يُحدَّث أكثر للقراءة حصرياً من العنصر الجديد بينما يستمر بالكتابة لكليهما للأمان. فقط بمجرد تأكيد أن كل نسخة قيد التشغيل قد ترحّلت بالكامل لاستخدام عنصر المخطط الجديد حصرياً، تزيل مرحلة الانكماش العنصر القديم.
إعادة تعبئة البيانات الموجودة دون قفل الجدول
إضافة عمود جديد رخيصة عادة، لكن ملؤه بقيم صحيحة لكل صف موجود — خطوة إعادة التعبئة — يمكن أن تكون عملية ثقيلة فعلاً على جدول كبير، وتشغيلها كبيان تحديث واحد كبير يخاطر بقفل الجدول لمدة تصبح هي نفسها انقطاعاً فعلياً. استراتيجيات إعادة التعبئة الآمنة للإنتاج تعالج بدلاً من ذلك الصفوف بدفعات صغيرة، مع فترات توقف بين الدفعات، مراقبة تأخر التكرار وحمل قاعدة البيانات طوال العملية.
تنسيق تغييرات المخطط مع عمليات نشر التطبيق
الترحيل بلا توقف يعمل فقط إذا كانت تغييرات المخطط وعمليات نشر كود التطبيق مُرتَّبة بشكل صحيح نسبياً لبعضها، والحصول على هذا الترتيب خاطئاً مصدر شائع لحوادث كان يمكن منعها. كقاعدة عامة، تغيير مخطط يضيف شيئاً يحتاج للنشر والتأكد من استقراره قبل نشر كود تطبيق يعتمد على العنصر الجديد، بينما تغيير مخطط يزيل شيئاً يحتاج للانتظار حتى تُقاعَد كل نسخة تطبيق تعتمد على العنصر القديم بالكامل.
معالجة الترحيلات التي لا يمكن جعلها فورية
بعض تغييرات المخطط أثقل بطبيعتها من غيرها بغض النظر عن مدى العناية في تدريجها: إضافة فهرس لجدول كبير جداً، أو تغيير نوع بيانات عمود، يمكن أن يستغرق وقتاً طويلاً فعلاً حتى عند دعم قاعدة البيانات لفعل ذلك دون قفل جدول كامل. توفّر معظم أنظمة قواعد البيانات الناضجة أنواعاً متصلة أو متزامنة من هذه العمليات مصممة خصيصاً لتجنب حجب القراءات والكتابات أثناء العملية، لكن هذه الأنواع تأتي غالباً بتحفظاتها الخاصة.
بالنسبة لتغييرات المخطط الضخمة فعلاً على جداول كبيرة جداً، تتبنى بعض المؤسسات استراتيجية الجدول الظل: إنشاء جدول جديد كلياً بالمخطط المستهدف، إعادة تعبئته من الجدول الأصلي، الحفاظ على تزامن الاثنين عبر مُشغِّلات أو كتابات مزدوجة أثناء الفترة الانتقالية، ثم إجراء تحويل ذري بمجرد أن يكون الجدول الظل قد لحق بالكامل وتم التحقق من اتساقه.
اختبار الترحيلات قبل وصولها للإنتاج
ترحيل مخطط لم يُختبَر أبداً ضد نسخة واقعية من بيانات الإنتاج مقامرة حقيقية، لأن سلوك الترحيل — كم يستغرق ملء البيانات، وهل يكتمل بناء الفهرس بسلاسة، وهل تُدخل الكتابة المزدوجة تنافساً غير متوقع — يعتمد بشدة على حجم وتوزيع البيانات بطرق لا يمكن لقاعدة بيانات تطوير صغيرة كشفها ببساطة. تحتفظ الفرق الناضجة ببيئة اختبار بحجم بيانات يماثل الإنتاج تحديداً حتى يمكن تجربة الترحيلات هناك أولاً، كاشفة مشاكل التوقيت والقفل قبل أن تصبح حوادث حية بدلاً من بعدها.
هذه التجربة قيّمة بشكل خاص لالتقاط نمط فشل محدد وشائع: ترحيل يعمل بشكل مثالي على جدول صغير في التطوير لكنه يتوقف، أو يقفل بشكل غير مقبول، أو ببساطة يستغرق وقتاً أطول بكثير من المتوقع بمجرد تشغيله ضد جدول بأعداد صفوف وبنى فهرسة إنتاج حقيقية.
استراتيجية التراجع لتغييرات المخطط
كل خطة ترحيل تحتاج إجابة صادقة عما يحدث إذا سار شيء خطأ في منتصف الطريق، وهذه الإجابة تبدو مختلفة في كل مرحلة من ترحيل التوسيع-الانكماش. أثناء مرحلة التوسيع، التراجع عادة مباشر، لأن المخطط القديم ومسار الكود القديم يبقيان سليمين تماماً وغير مُمَسَّين؛ يمكن ببساطة ترك العنصر الجديد في مكانه غير مستخدَم، أو إزالته، دون التأثير على التطبيق قيد التشغيل على الإطلاق. يصبح التراجع أكثر دقة بمجرد أن يبدأ كود التطبيق بالكتابة إلى أو القراءة من عنصر المخطط الجديد.
الممارسة الأكثر أماناً بشكل عام هي معاملة مرحلة الانكماش — نقطة اللاعودة حيث تُزال عناصر المخطط القديمة فعلياً — كباب أحادي الاتجاه لا يُفتَح إلا بعد فترة طويلة فعلاً من الثقة في المسار الجديد، غالباً بعد أن تعمل المراقبة لأسابيع بدلاً من ساعات، تحديداً لأنها المرحلة الوحيدة في العملية بأكملها التي لا يمكن عكسها بسهولة إذا اكتُشفت مشكلة بعد فوات الأوان.
مثال عملي: تقسيم جدول مستخدمين متكامل بلا توقف
نما جدول `users` الأصلي لشركة تجارة إلكترونية عبر سنوات ليضم عشرات الأعمدة الممتدة عبر بيانات مصادقة، وعناوين شحن، وتفضيلات تسويقية، وإعدادات حساب، وأراد الفريق تقسيمه إلى عدة جداول أصغر وأكثر تركيزاً لكل من الوضوح ولأن بعض الأعمدة كانت تُستعلَم وتُحدَّث بتكرار أكبر بكثير من غيرها. إجراء هذا التقسيم خلال فترة صيانة لم يكن مقبولاً نظراً لقاعدة عملاء الشركة العالمية، لذا طبّق الفريق التوسيع-الانكماش عمداً وعلى مراحل.
أنشأوا أولاً الجداول الجديدة الأكثر تركيزاً جنباً إلى جنب مع جدول `users` الموجود، دون لمس الأصلي على الإطلاق، ثم حدّثوا كود التطبيق للكتابة إلى كل من الجدول القديم والجداول الجديدة المناسبة في آن واحد لكل إنشاء وتحديث حساب. عملية إعادة تعبئة مُدرَّجة بعناية، مُقيَّدة للعمل فقط خلال ساعات الحمل المنخفض ومُنسَّقة مع مراقبة تأخر التكرار في الوقت الفعلي، ملأت الجداول الجديدة بسنوات عديدة من تاريخ حسابات الشركة الموجودة على مدى نحو أسبوع. بمجرد اكتمال إعادة التعبئة والتحقق من اتساقها مقابل الجدول الأصلي، حُدِّث كود التطبيق للقراءة من الجداول الجديدة بينما استمر بالكتابة المزدوجة لفترة أمان إضافية، وفقط بمجرد أن أكدت المراقبة أن لا مسار كود لا يزال يقرأ الأعمدة الزائدة الآن في الجدول العريض الأصلي، أزال الفريق تلك الأعمدة، بعد أشهر من بدء العملية. اكتملت الهجرة بأكملها دون دقيقة واحدة من التوقف المواجه للعملاء، بتكلفة جدول زمني أطول وأكثر تأنياً مما كان سيتطلبه نهج نافذة الصيانة.
وبتكلفة تدريب تشغيلي حقيقي على كيفية تنفيذ ترحيلات كبيرة بأمان، أصبحت هذه التجربة نموذجاً مرجعياً استخدمه الفريق لكل عملية تقسيم جدول كبيرة لاحقة أجروها.
الخلاصة
الترحيل بلا توقف لقاعدة البيانات يتعلق أقل بأي تقنية ذكية واحدة وأكثر بعادة ذهنية منضبطة: معاملة تغيير مخطط ليس كحدث ذري واحد بل كتسلسل من خطوات آمنة فردياً، كل واحدة تترك قاعدة البيانات في حالة يمكن فيها لكود التطبيق قيد التشغيل حالياً وذلك المزمع نشره قريباً العمل بشكل صحيح. التوسيع-الانكماش، وإعادة التعبئة المُتحكَّم فيها بعناية، والتسلسل المتعمد بين تغييرات المخطط ونشر التطبيق، تتيح معاً للفرق تطوير بنية قاعدة بيانات حية باستمرار، دون الحاجة أبداً لنافذة الصيانة التي اعتمدت عليها الأساليب الأبسط سابقاً.