labZero:~$ cd ./notes / note-alter-table-product...

الملاحظات
نسخة صفر 5 يونيو 2026

متى يصبح تعديل الـ schema في الإنتاج أخطر من المشكلة التي يحلّها؟

أضفت عموداً لجدول نصف مليون صف — الـ migration نجح محلياً في ثانية واستغرق 4 دقائق على الإنتاج وقفل الجدول كاملاً.

#Laravel #MySQL #Migrations

في مشروع فواتير بعد ستة أشهر من الإنتاج، احتجت إضافة عمود approved_by بقيمة NOT NULL لجدول invoices الذي وصل إلى 520 ألف صف. كتبت migration عادية، شغّلتها محلياً على 200 صف — أقل من ثانية. نشرتها في الإنتاج وقت الذروة.

قُفل الجدول. أربع دقائق كاملة لا يستطيع أحد فتح فاتورة أو إنشاء واحدة. ALTER TABLE invoices ADD COLUMN approved_by BIGINT NOT NULL DEFAULT 0 يُحدث full table rebuild في InnoDB لأن إضافة NOT NULL دون default متاحة مسبقاً في القاموس تُلزم MySQL بكتابة كل الصفوف.

الجوهر: المشكلة لم تكن في الـ migration نفسها، بل في افتراضي أن ما ينجح على بيانات صغيرة ينجح بنفس الأثر على بيانات حقيقية. اكتشفت أن الحل الصحيح لجداول الإنتاج الكبيرة ثلاث خطوات: أولاً، أضف العمود nullable أو مع DEFAULT مسبق حتى يستخدم MySQL metadata-only change. ثانياً، شغّل backfill job على دفعات. ثالثاً، عدّل القيد بعد اكتمال الـ backfill.

// الخطوة 1: nullable أولاً — لا قفل
$table->unsignedBigInteger('approved_by')->nullable();
// الخطوة 3: بعد الـ backfill
$table->unsignedBigInteger('approved_by')->nullable(false)->change();

الآن قبل أي migration أسأل سؤالاً واحداً: هل هذا تغيير في القاموس فقط أم يُعيد MySQL كتابة الصفوف؟ وكل ADD COLUMN NOT NULL على جدول يتجاوز عشرة آلاف صف يأخذ مسار الثلاث خطوات بلا استثناء.

لديك مشكلة مشابهة؟

إذا واجهت موقفًا قريبًا من هذا في مشروعك، يمكننا الحديث عنه.

لنتحدّث عنها