النسخ المنطقي في PostgreSQL: قاعدة احتياطية حية بلا توقف

النسخ المنطقي في PostgreSQL ينقل تغييرات الصفوف عبر publication وsubscription إلى خادم ثانٍ حي، حتى لو كان بإصدار أحدث، لذلك هو أنسب أداة للترقية بين الإصدارات الرئيسية ونقل القواعد بانقطاع ثوانٍ فقط.
الجواب المختصر
النسخ المنطقي (logical replication) في PostgreSQL ينقل التغييرات على مستوى الصفوف من قاعدة "ناشرة" إلى قاعدة "مشتركة" عبر ثنائية publication وsubscription، بدل نسخ ملفات WAL كما هي بالبايت. النتيجة خادم ثانٍ حي يقبل الاستعلامات، ويمكن أن يعمل بإصدار PostgreSQL مختلف عن الأصل، ويمكن أن يستقبل جداول محددة فقط بدل القاعدة كاملة.
هذه الخصائص تجعله الخيار الأنسب في ثلاث حالات: ترقية بين إصدارين رئيسيين (من 13 إلى 17 مثلاً) بانقطاع لا يتجاوز من 5 إلى 30 ثانية، ونقل قاعدة بيانات إلى خادم جديد والنظام شغال، وتغذية قاعدة تقارير بجداول بعينها دون أن تثقل القاعدة الأساسية. أما إن كان هدفك نسخة مطابقة تتحول إليها تلقائياً عند سقوط الخادم الأساسي، فالنسخ الفيزيائي (streaming replication) أبسط وأشمل لأنه ينقل كل شيء بما فيه تغييرات البنية والتسلسلات.
الإعداد نفسه ثلاث خطوات: اضبط wal_level = logical على الناشر، ثم أنشئ publication بالجداول المطلوبة، ثم أنشئ subscription على الخادم الثاني يشير إلى الأول. بعدها يبدأ نسخ أولي للبيانات ثم تتدفق التغييرات بتأخر يقاس عادة بالمللي ثانية بين خادمين في نفس المركز.
الفيزيائي أم المنطقي: الفرق الذي يحدد اختيارك
النسخ الفيزيائي يرسل سجل WAL كما هو، فيصبح الخادم الثاني نسخة طبق الأصل بالبايت من الأول. هذا يعني نفس الإصدار الرئيسي ونفس المعمارية، ولا يمكنك الكتابة عليه إطلاقاً لأنه في وضع القراءة فقط. ميزته أنه لا يحتاج تفكيراً: كل جدول جديد وكل فهرس وكل تسلسل ينتقل وحده.
النسخ المنطقي يفك سجل WAL إلى عمليات منطقية (إدراج وتحديث وحذف على صف محدد) ويرسلها للمشترك الذي ينفذها كأنها معاملات عادية. لذلك يستطيع المشترك أن يملك جداول إضافية خاصة به، وفهارس مختلفة، وإصداراً أحدث. الثمن أن بعض الأشياء لا تنتقل، وهذا ما سنفصله لاحقاً.
| المعيار | النسخ الفيزيائي | النسخ المنطقي |
|---|---|---|
| ما يُنقل فعلياً | كتل WAL بالبايت | تغييرات الصفوف |
| اختلاف الإصدار الرئيسي | غير ممكن | ممكن |
| نسخ جزء من الجداول | لا، القاعدة كاملة | نعم، جدولاً جدولاً |
| الكتابة على الخادم الثاني | ممنوعة | مسموحة على جداول أخرى |
| تغييرات البنية DDL | تنتقل تلقائياً | لا تنتقل |
| التسلسلات sequences | تنتقل | لا تنتقل في الإصدارات الشائعة |
| الاستخدام الأمثل | تحويل عند الفشل | ترقية ونقل وتقارير |
القاعدة العملية: للتوافرية العالية اختر الفيزيائي، وللحركة بين خادمين أو إصدارين اختر المنطقي. كثير من الأنظمة الكبيرة تستخدم الاثنين معاً، فيزيائي للتحويل السريع ومنطقي لتغذية مستودع التقارير.
الإعداد خطوة بخطوة
على الخادم الناشر
في ملف postgresql.conf اضبط wal_level = logical، وارفع max_replication_slots وmax_wal_senders إلى 10 مثلاً، فكل اشتراك يستهلك slot واحداً ومرسلاً واحداً، والنسخ الأولي يستهلك أكثر مؤقتاً. تغيير wal_level يحتاج إعادة تشغيل كاملة للخدمة، فخطط لها في نافذة صيانة قصيرة قبل أي شيء آخر.
أنشئ مستخدماً مخصصاً للنسخ بصلاحية REPLICATION وصلاحية SELECT على الجداول المنشورة، ثم أضف سطراً في pg_hba.conf يسمح له بالاتصال من عنوان IP الخادم الثاني فقط، مع scram-sha-256 لا md5. لا تفتح المنفذ 5432 للعالم، فالجدار الناري يجب أن يسمح بالخادم المشترك وحده.
بعدها أنشئ النشر: CREATE PUBLICATION erp_pub FOR TABLE orders, invoices, customers; أو FOR ALL TABLES إن كنت تنقل القاعدة كاملة. انتبه لشرط مهم: كل جدول يستقبل UPDATE أو DELETE يحتاج مفتاحاً أساسياً أو REPLICA IDENTITY محدداً، وإلا سترفض القاعدة الناشرة عمليات التحديث على ذلك الجدول نفسه، وهذا يكسر التطبيق لا النسخ فقط. افحص الجداول بلا مفتاح أساسي قبل البدء.
على الخادم المشترك
النسخ المنطقي لا ينقل البنية، فابدأ بنقلها يدوياً: pg_dump --schema-only من الناشر ثم استعادتها في المشترك. بعدها: CREATE SUBSCRIPTION erp_sub CONNECTION 'host=10.0.0.5 dbname=erp user=repl' PUBLICATION erp_pub; وسيبدأ فوراً بنسخ أولي لكل جدول ثم الانتقال لتدفق التغييرات.
النسخ الأولي لقاعدة حجمها 50GB يستغرق عادة من 20 إلى 60 دقيقة على أقراص SSD حسب عدد الفهارس، لأن المشترك يبني الفهارس أثناء الإدراج. حيلة مفيدة: احذف الفهارس الثانوية الثقيلة من المشترك قبل الاشتراك وأعد بناءها بعد انتهاء النسخ الأولي، فهذا يختصر الوقت أحياناً إلى النصف.
مراقبة التأخر بالبايت
على الناشر، الاستعلام الأهم هو: SELECT application_name, pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes FROM pg_stat_replication; الذي يعطيك المسافة بالبايت بين آخر ما كتبه الناشر وآخر ما طبقه المشترك. في التشغيل الطبيعي تبقى القيمة تحت 1MB، فإن تجاوزت 100MB وظلت تكبر فالمشترك لا يلحق، والسبب غالباً قرص بطيء أو معاملة ضخمة أو تعارض أوقف الاشتراك.
الخطر الحقيقي في replication slot المهجور. الـslot يجبر الناشر على الاحتفاظ بكل WAL لم يستهلكه المشترك، فإن توقف المشترك يومين ستمتلئ أقراص الناشر وتتوقف القاعدة الأساسية كلها. راقب pg_replication_slots وعمود active، واضبط max_slot_wal_keep_size (متاح من الإصدار 13) على حد مثل 20GB حتى يتخلى الناشر عن الـslot قبل أن يخنق القرص.
على المشترك راجع pg_stat_subscription لمعرفة آخر LSN مستلم ووقت آخر رسالة. اربط هذه الأرقام بأداة المراقبة عندك بتنبيه عند 50MB تأخر أو slot غير نشط لأكثر من 10 دقائق، فهذه التنبيهات تنقذك من أسوأ سيناريو.
الحدود التي يجب أن تعرفها قبل الاعتماد عليه
- تغييرات البنية لا تنتقل: أي ALTER TABLE أو جدول جديد يجب تنفيذه على المشترك أولاً ثم الناشر، وإلا سيفشل تطبيق الصفوف التي تحمل عموداً لا يعرفه المشترك.
- التسلسلات لا تنتقل: قيم الصفوف تصل كما هي، لكن عداد التسلسل في المشترك يبقى على 1. هذا لا يضر أثناء النسخ، لكنه يكسر أول إدراج بعد التحويل إن لم تزامنه يدوياً.
- الكائنات الكبيرة large objects لا تنتقل، فإن كان نظامك يخزن ملفات بها فالنسخ المنطقي لا يكفي وحده.
- التعارضات توقف الاشتراك: صف مكرر يخالف قيداً فريداً في المشترك يوقف التطبيق كاملاً حتى تتدخل، ومن الإصدار 15 يمكنك تجاوز معاملة بعينها بـALTER SUBSCRIPTION ... SKIP.
- المعاملات الضخمة: تحديث 10 ملايين صف بمعاملة واحدة يصل المشترك دفعة واحدة بعد الـCOMMIT، فيقفز التأخر فجأة. خيار streaming = on في الإصدارات الحديثة يخفف ذلك.
ترقية إصدار رئيسي بانقطاع ثوانٍ
الطريقة التقليدية للترقية هي pg_dump ثم الاستعادة، وهذه تعني ساعات توقف لقاعدة بحجم 100GB. الطريقة الأسرع pg_upgrade في المكان نفسه، لكنها تحتاج توقفاً أيضاً ولا تسمح بالرجوع بسهولة. النسخ المنطقي يحول الترقية إلى عملية تحويل بسيطة:
- جهز خادماً جديداً بالإصدار الأحدث، وانقل إليه البنية بـpg_dump --schema-only.
- أنشئ publication لكل الجداول على القديم وsubscription على الجديد، وانتظر انتهاء النسخ الأولي.
- راقب التأخر حتى يستقر قريباً من الصفر، واختبر التطبيق على الجديد بقراءات فعلية.
- أوقف الكتابة على القديم بوضع الصيانة في التطبيق، وانتظر حتى يصبح lag_bytes صفراً.
- زامن التسلسلات: اقرأ قيمة كل تسلسل من القديم واضبطها على الجديد بـsetval مع هامش 1000 للاحتياط.
- غيّر عنوان الاتصال في التطبيق إلى الخادم الجديد، ثم احذف الاشتراك.
الانقطاع الفعلي هنا هو المدة بين إيقاف الكتابة وتحويل الاتصال، وفي التجارب المعتادة من 5 إلى 30 ثانية. الأهم أن الخادم القديم يبقى سليماً، فإن ظهرت مشكلة بعد التحويل تعيد التطبيق إليه خلال دقيقة.
حجم الخادمين من باقات كلاود
النسخ المنطقي يحتاج خادمين متقاربين في القدرة، لأن المشترك يطبق نفس حجم الكتابة الذي يستقبله الناشر. لنظام ERP بقاعدة حجمها نحو 50GB واستخدام متوسط، باقة أعمال (4 vCPU و8GB RAM و40GB SSD و1TB نقل) بـ11,500 ريال سنوياً مع إضافة +100GB SSD بـ500 ريال لكل خادم تكفي بارتياح. للأنظمة الأصغر تحت 15GB، باقة نمو (2 vCPU و4GB RAM) بـ6,900 ريال سنوياً تؤدي المهمة.
الخادمان في نفس مركز الرياض يعني تأخر شبكة داخلي أقل من مللي ثانية، فيبقى lag_bytes قريباً من الصفر حتى تحت ضغط الكتابة. بعد الترقية لا ترمِ الخادم القديم: حوّله إلى نسخة فيزيائية احتياطية أو خادم تقارير. تفاصيل الموارد في صفحة الخادم السحابي، والمقارنة الكاملة في الأسعار والباقات.
الخلاصة: ماذا تفعل الآن
- إن كان هدفك تحويلاً عند الفشل فقط، ابدأ بالنسخ الفيزيائي ولا تعقّد الأمر.
- إن كانت أمامك ترقية إصدار أو نقل خادم، افحص الجداول بلا مفتاح أساسي اليوم، فهي العائق الأول.
- اضبط max_slot_wal_keep_size قبل إنشاء أي slot، وأضف تنبيه التأخر قبل الإطلاق لا بعده.
- جرّب العملية كاملة على نسخة اختبار، وقس زمن النسخ الأولي وزمن التحويل بنفسك قبل نافذة الإنتاج.


