قواعد البيانات العلائقية وضمانات ACID
قواعد البيانات العلائقية وضمانات ACID
تُشكّل قواعد البيانات العلائقية العمود الفقري لأنظمة الإنتاج منذ سبعينيات القرن الماضي. فـPostgreSQL يشغّل GitHub وInstagram وShopify، وMySQL يدير WordPress وعدداً لا يُحصى من منتجات SaaS. إن فهم لماذا صُمِّمت على هذا النحو — الجداول والمفاتيح والعلاقات وعقد ACID — أمرٌ لا غنى عنه قبل أن تتمكن من اتخاذ قرارات مستنيرة حول متى تستخدمها ومتى تلجأ إلى بديل آخر.
النموذج العلائقي باختصار
الجدول (Relation) هو مجموعة من السجلات (الصفوف) التي تشترك جميعها في نفس المخطط (الأعمدة). لكل عمود نوع بيانات محدد وقيود اختيارية. كل صف في الجدول المُصمَّم تصميماً سليماً يُعرَّف بشكل فريد بواسطة المفتاح الأساسي (Primary Key) — وهو مجموعة أدنى من الأعمدة التي لا تتكرر قيمها بين الصفوف.
تترابط الجداول ببعضها من خلال المفاتيح الأجنبية (Foreign Keys): عمود في جدول يُشير إلى المفتاح الأساسي لجدول آخر. يُطبّق ذلك تكامل المراجع — لا يمكنك إنشاء طلب لعميل غير موجود، ولا يمكنك حذف عميل لديه طلبات (إلا إذا استخدمت CASCADE أو SET NULL).
التطبيع: لماذا نتجنب التكرار
التطبيع (Normalization) هو عملية هيكلة الجداول لتقليل ازدواجية البيانات ومنع تناقضات التحديث. المثال الكلاسيكي: إذا خزّنت بريد العميل الإلكتروني في كل صف من جدول orders، فعند تغيير البريد ستحتاج إلى تحديث آلاف الصفوف — وإذا فاتك صف واحد أصبحت بياناتك متناقضة. الاحتفاظ به في مكان واحد (جدول customers) يعني تحديثاً واحداً صحيحاً دائماً.
القاعدة العملية: كل عمود غير مفتاحي يجب أن يعتمد على المفتاح كاملاً لا جزءاً منه. عملياً، ضع كل كيان في جدوله الخاص وأشر إليه بالمعرّف.
المعاملات: وحدة العمل
المعاملة (Transaction) هي سلسلة من عبارات SQL تتعامل معها قاعدة البيانات كعملية منطقية واحدة. المثال الكلاسيكي هو التحويل البنكي: خصم من حساب A، وإضافة إلى حساب B. يجب أن ينجح الأمران معاً، أو لا يُثبَّت أيٌّ منهما. لا تريد أبداً حالة خُصم فيها A دون أن يُضاف لـB.
تضمن قاعدة البيانات أن المعاملة إما تُنفَّذ بالكامل، أو يُتراجع عنها تاركةً كلا الصفين دون تغيير. هذا هو أساس عقد ACID.
ACID — الضمانات الأربع
ACID اختصار لأربع خصائص يجب أن تحققها كل معاملة علائقية. كل خاصية تحل نمطاً محدداً من أنماط الفشل.
نظرة أعمق على كل خاصية
الذرية (Atomicity) — تستخدم قاعدة البيانات سجل الكتابة المسبقة (WAL). قبل تغيير أي صفحة بيانات على القرص، تُكتب التغييرات المقصودة في WAL. إذا انهار الخادم في منتصف المعاملة، تُعيد عملية الاسترداد تنفيذ المعاملات المكتملة وتتراجع عن غير المكتملة. من منظور التطبيق، إما أن المعاملة حدثت بالكامل أو لم تحدث أصلاً.
الاتساق (Consistency) — هذا جزئياً مسؤولية قاعدة البيانات (تطبيق القيود) وجزئياً مسؤولية التطبيق (عدم انتهاك قواعد العمل). يفحص PostgreSQL قيود NOT NULL وUNIQUE وCHECK وFOREIGN KEY عند الإيداع. إذا فشل أي فحص، تُلغى المعاملة بالكامل تلقائياً.
العزل (Isolation) — هو أكثر الخصائص دقة. يحدد معيار SQL أربعة مستويات عزل تُوازن بين الصحة والتزامن:
- Read Uncommitted — يمكن رؤية الكتابات غير المُثبَّتة من المعاملات الأخرى. نادراً ما يُستخدم.
- Read Committed — يرى فقط البيانات المُثبَّتة. الافتراضي في PostgreSQL وأغلب الأنظمة. يمنع القراءات غير النظيفة، لكن يسمح بـالقراءات غير القابلة للتكرار.
- Repeatable Read — يُؤخذ لقطة من البيانات عند بدء المعاملة؛ إعادة القراءات مستقرة. الافتراضي في MySQL/InnoDB.
- Serializable — تُنفَّذ المعاملات كما لو كانت متسلسلة. أعلى صحة، وأعلى تكلفة. استخدمها للدفاتر المالية وخصم المخزون.
تُطبّق معظم قواعد البيانات الحديثة العزل من خلال MVCC (التحكم في التزامن متعدد الإصدارات): كل كاتب ينشئ إصداراً جديداً من الصف بدلاً من قفله في مكانه. يرى القرّاء الإصدار الحالي وقت لقطتهم. يتيح ذلك تزامناً عالياً في القراءة دون حجب الكتابة.
الديمومة (Durability) — لا يعود COMMIT بنجاح حتى يُدفَع سجل WAL إلى تخزين دائم (fsync على القرص أو SSD). لهذا قد تستغرق الإيداعات على الأقراص الدوارة ميلي ثانية. قواعد البيانات السحابية (Aurora, Cloud SQL) تُكرّر سجلات WAL عبر مناطق توفر متعددة — يعود الإيداع فقط بعد أن تُقرّ نسبة أغلبية من العقد، مما يمنح ديمومة تصمد أمام فشل مركز بيانات كامل.
ثمن ACID: المقايضات مع الأداء
ضمانات ACID ليست مجانية. لكل خاصية ثمنها:
- الذرية والديمومة — كل إيداع يتطلب
fsync. على عقدة واحدة، يتحمل PostgreSQL نحو 1,000 إلى 5,000 معاملة كتابة في الثانية قبل أن يصبح WAL عائق الأداء. استخدام الإيداع الجماعي (تجميع عدة معاملات في fsync واحد) يرفع ذلك إلى عشرات الآلاف. - العزل — تتطلب مستويات العزل الأعلى مزيداً من القفل أو إصدارات MVCC أكثر، مما يزيد ضغط الذاكرة واحتمال الجمود. يمكن أن يُخفّض العزل القابل للتسلسل الإنتاجية بنسبة 30–50% مقارنةً بـRead Committed على قاعدة بيانات OLTP نشطة.
- الاتساق — تتطلب قيود المفاتيح الأجنبية بحث فهرس عند كل
INSERTأوDELETE. إزالة المفاتيح الأجنبية تزيل هذا الحمل — لكنها تزيل الشبكة الآمنة أيضاً.
synchronous_commit = off في PostgreSQL، مما يعني عودة COMMIT قبل دفع WAL. يُحسّن ذلك زمن استجابة الكتابة، لكنه يُدخل نافذة خطر تصل إلى ~200 مللي ثانية من فقدان البيانات المُيدَعة عند التعطل. هذه مقايضة صحيحة فقط لبيانات يمكن إعادة توليدها (أحداث تحليلية، استيعاب سجلات) — وليس أبداً للبيانات المالية أو سجلات المستخدمين.
اختيار قاعدة بيانات علائقية للتوسع
عند الحجم الصغير (ملايين الصفوف، بضع مئات من الاستعلامات في الثانية) أي قاعدة بيانات علائقية مُفهرَسة بشكل صحيح تكون سريعة. عند التوسع:
- توسعة القراءة — أضف نسخاً للقراءة (الدرس 5). تتوزع القراءات؛ الكتابات لا تزال تذهب إلى مصدر واحد.
- توسعة الكتابة — التوسع الرأسي (جهاز أكبر) يمنح وقتاً. بعد ذلك، التقسيم والتشارد (الدرس 6) أو نقل بعض أعباء العمل إلى مخازن متخصصة.
- تجميع الاتصالات — يُولّد PostgreSQL عملية لكل اتصال؛ عند 10,000 اتصال يصل استخدام الذاكرة وحده إلى ~10 جيجابايت. PgBouncer (تجميع وضع المعاملة) يتيح لآلاف خيوط التطبيق مشاركة عشرات اتصالات قاعدة البيانات الفعلية.
قواعد البيانات العلائقية ليست تقنية موروثة — بل هي الخيار الافتراضي الصحيح للبيانات المنظمة والعلائقية حيث تهم الصحة. عقد ACID هو ما يجعلك تثق بالأرقام التي تقرأها من قاعدة البيانات، وهو بالضبط الأساس الذي تُبنى عليه كل قرارات التصميم الأخرى في هذه الدورة.