مبدأ الـ Durability
السلام عليكم ورحمة الله وبركاته
يمكنك متابعة السلسلة بالترتيب أو الانتقال مباشرة إلى أي مقال:
المقدمة
حسنًا لقد وصلنا لأخر مبدأ في الـ ACID وهو مبدأ الـ Durability
لا تقلق لن أطيل عليك سيكون خفيفًا ونظريًا مثل مبدأ الـ Consistency
لأن مبدأ الـ Durability غالبًا ما يكون موجه للأشخاص الذين يقومون بتطوير الـ Database Management System مثل MySQL أو PostgreSQL أو MongoDB
وكيف يحققون هذا المبدأ في قواعدهم
لكن يمكنك تحقيقه بنفسك عن طريق توفير نوع من الـ Backup لقاعدة البيانات الخاصة بك
أو تصميم الـ Database بشكل جيد وجعل كل Query تكون Optimized وسريعة وأداءها عالي
لتقليل فرص حدوث مشاكل متعلقة بالبطيء أو Query ثقيلة تسبب Timeout أو Deadlock كما ذكرنا سابقًا
مبدأ Durability هو آخر مبدأ من مبادئ الـ ACID، وهو يضمن أن جميع العمليات التي تمت داخل Transaction لن تُفقد حتى في حالة حدوث عطل أو انقطاع مفاجئ في النظام
بمعنى آخر، عند تنفيذ Transaction بنجاح ووصولها إلى مرحلة COMMIT، فإن جميع التعديلات على قاعدة البيانات يجب أن تُحفظ فعلًا في الـ Database
لأنك عندما تقول لي أنك قمت بعمل COMMIT بنجاح فهذا يعني أنك فعلًا قمت بتنفيذ الـ Transaction بنجاح وقمت بحفظ البيانات في الـ Database
لا تقول لي لقد تم العملية بنجاح وبعد لحظات يحدث انقطاع في الكهرباء أو أن الـ Server تعطل
ثم نفاجيء بأن البيانات لم يتم حفظها فعليًا برغم من أنك قمت بعمل COMMIT وجاءتك رسالة أن العملية تمت بنجاح
هذا ما يعنيه مبدأ الـ Durability أنك عندما تقول لي أنك قمت بعمل COMMIT بأنك تضمن لي أن البيانات قد تم حفظها
ولو حدث أي كارثة فإن البيانات ستكون موجودة ولن تفقد
كيف يعمل مبدأ Durability ؟
كل أنظمة قواعد البيانات سواء كانت MySQL أو PostgreSQL أو MongoDB تحاول تحقيق مبدأ Durability بطرق مختلفة
بعض الأنظمة تخزن البيانات في الذاكرة العشوائية RAM وبعد ما تقوم بعمل COMMIT تقوم بنقل البيانات من الـ RAM إلى الـ Database
وهذا غير مضمون للـ Durability لأن طالما أن البيانات والتعديل في الـ RAM فسوف تفقدها في حالة حدوث أي عطل
الأسلوب الأفضل هو أن تخزنها في الـ Hard Disk مباشرة ثم بعد الـ COMMIT تقوم بنقلها إلى الـ Database
لذا في حالة حدوث أي عطل فإن البيانات ستكون موجودة في الـ Hard Disk ويمكنك استعادتها
تقنيات تحقيق الـ Durability
يمكننا تحقيق مبدأ الـ Durability بعدة تقنيات وأساليب، منها:
Write-Ahead Logging (WAL)
تسجل جميع العمليات في ملف Log File في الـ Hard Disk قبل تنفيذها أو نقلها إلى الـ Database مما يتيح استعادة البيانات في حال حدوث أي خطأ
يعد الأكثر شيوعًا واستخدامًا في تحقيق مبدأ الـ Durability
وطريقة عمله ببساطة هي أنه يقوم بتسجيل جميع العمليات التي تمت داخل الـ Transaction في ملف Log قبل تنفيذها
ثم في حالة الـ COMMIT يقوم بنقل البيانات من الـ Log إلى الـ Database
في حالة حدوث أي عطل فإنه يمكنه استعادة البيانات من الـ Log
شكل الملف يكون أشبه بملفات الـ Migration الخاصة بالـ Database التي تحدثنا عنها في مقالة شرح الـ Database Migration لإدارة التغييرات
[2025-02-17 10:00:00] TXN-ID: 101 | BEGIN
[2025-02-17 10:00:01] TXN-ID: 101 | UPDATE users SET balance = 500 WHERE id = 5
[2025-02-17 10:00:02] TXN-ID: 101 | COMMIT
[2025-02-17 10:05:00] TXN-ID: 102 | BEGIN
[2025-02-17 10:05:01] TXN-ID: 102 | INSERT INTO orders (product, price) VALUES ('Laptop', 1500)
[2025-02-17 10:05:02] TXN-ID: 102 | COMMIT
هذا مثال بسيط على كيفية تسجيل العمليات في ملف Log
وبالطبع هذا شكل تخيلي لأن الـ Log يحتوي على العديد من المعلومات الأخرى ويختلف شكل الـ Log من قاعدة بيانات لأخرى
وهذه الطريقة ستجدها في كل DBMS تقريبًا لكن بأسماء مختلفة مثل:
- ملف
WALفيPostgreSQL - ملف
BinlogفيMySQL - ملفات
idfوmdfفيSQL Server
ملحوظة: هذه الملفات تحتفظ بكل العمليات التي تمت في الـDatabase
حتى لو قمت بحذف بعض البيانات من الـDatabaseسواء تم تنفيذها داخلTransactionأو لا
لذا أحيانًا تستطيع استعادة البيانات بعد حذفها لكن استعادتها ستكون بشكل يدوي عن طريق قراءة هذه الملفات وتنفيذ العمليات التي تمت فيها
Asynchronous Snapshots
يقوم بعمل نسخة احتياطية من الـ Database بشكل دوري ومتكرر لضمان عدم فقدان البيانات في حالة الأعطال
بالتالي عند حدوث أي عطل يمكنك استعادة البيانات من أحدث نسخة احتياطية والأمر يكون أشبه بالـ Backup
فلو افترضنا أن السيرفر تعطل في الساعة 03:00 وآخر نسخة احتياطية Snapshot كانت في الساعة 02:30
فبديهيًا أنك يمكنك استعادة البيانات من هذه النسخة الاحتياطية فقط بالتالي ستفقد البيانات التي تمت بعد الساعة 02:30
في Redis تستخدم هذه التقنية مع الـ Append Only Files لتحقيق مبدأ الـ Durability
وبالطبع قد تجمع بعض الـ Database Management System بين Write-Ahead Logging و Asynchronous Snapshots لتحقيق الـ Durability أيضًا
Append Only Files (AOF)
مثل الـ WAL لكنه يستخدم في Redis وما يشبهها لتسجيل تسلسل العمليات التي تم تنفيذها
في الحقيقة الـ Append Only Files تم تصميمه ليناسب Redis وشبيهاتها من قواعد البيانات التي تعتمد بشكل كبير على الـ In-Memory Storage
لكي يحقق مبدأ الـ Durability عن طريق مزامنة التغيرات التي تحدث في الـ Database مع الـ Append Only Files
كيف تحقق الـ Durability بنفسك ؟
بجناب ما ذكرناه سابقًا عن كيفية تحقيق الـ Durability داخل أنظمة قواعد البيانات مثل MySQL و PostgreSQL و MongoDB
يمكنك أنت أيضًا كمطور تطبيقات أن تحقق مبدأ الـ Durability بنفسك
سواء عن طريق الـ Backup أو عن طريق تصميم الـ Database بشكل جيد وجعل كل Query تكون Optimized وسريعة وأداءها عالي
لتقليل فرص حدوث مشاكل متعلقة بالبطيء أو Query ثقيلة تسبب Timeout أو Deadlock كما ذكرنا سابقًا
وبالتالي تقلل من فرص فقدان البيانات
أذكر مثال واجهته أحدى منصات التواصل الاجتماعي الشهيرة والتي كانت تعتمد على MySQL في تخزين البيانات الخاصة بالمستخدمين
وكان لديهم جدول للـ Posts وجدول للـ Comments وجدول للـ Likes
وكان هناك Foreign Key بين هذه الجداول لضمان سلامة البيانات
وكانوا كل مرة يريدون معرفة عدد الـ Likes على منشور معين يقومون بعمل JOIN بين جدول الـ Posts وجدول الـ Likes ويقوم باستخدام COUNT لحساب عدد الـ Likes
شيء أشبه بهذا الشكل:
SELECT COUNT(Likes.id) AS like_count
FROM Posts
JOIN Likes ON Posts.id = Likes.post_id
WHERE Posts.id = 123;
تخيل أن هذه الـ Query يتم تنفيذها ملايين المرات يوميًا على نفس المنشور
تحيل لو كان المنشور لديه ملايين الـ Likes وهناك عدد ضخم من المستخدمين قاموا بفتح المنشور في نفس الوقت
في هذه الحالة الـ Query ستكون ثقيلة جدًا وستسبب بطء في الأداء وربما تؤدي إلى Timeout كما حصل مع هذه المنصة بالفعل
حيث أن الـ Database لم تستطع التعامل مع هذا الكم الهائل من الـ JOIN والـ COUNT في نفس الوقت فبالتالي حدث Exception
هذا يعد من الأمور التي تنتهك مبدأ الـ Durability لأن المنشور قد لا يظهر عدد الـ Likes بشكل صحيح أو قد لا يظهر على الإطلاق
وبعض المستخدمين قد لا يستطيعون رؤية المنشور بسبب بطء الـ Database أو حدوث Exception
لذا الحل الذي اتبعته هذه المنصة هو أنهم قاموا بإنشاء عمود جديد في جدول الـ Posts يسمى like_count
وقاموا بتحديث هذا العمود في كل مرة يتم فيها إضافة Like جديد أو إزالة Like
وبالتالي أصبحوا لا يحتاجون إلى عمل JOIN و COUNT في كل مرة
بل فقد أصبحوا يقومون فقط بقراءة العمود like_count من جدول الـ Posts بشكل مباشر
SELECT like_count
FROM Posts
WHERE id = 123;
وبهذا الشكل تمكنوا من تحسين الأداء بشكل كبير وتقليل فرص حدوث Timeout أو Exception
وبالتالي حافظوا على مبدأ الـ Durability لأن البيانات أصبحت أكثر استقرارًا وسرعة في الوصول إليها
لذا تحسين تصميم الـ Database وتحسين الـ Query يمكن أن يساعدك في تحقيق مبدأ الـ Durability بنفسك