معالجة المعاملات
معالجة المعاملات
🌍 تشبيه من الحياة الواقعية
تخيّل تحويل بنكي — تحول 1000 من الحساب A إلى الحساب B:
- خصم 1000 من الحساب A
- إضافة 1000 إلى الحساب B
إذا نجحت الخطوة 1 لكن الخطوة 2 فشلت (مثل: انهيار النظام)، فإن A فقد أموالاً لكن B لم يستلمها — هذه مشكلة كبيرة.
المعاملة تربط هاتين الخطوتين في "عملية ذرية": إما أن تنجح الخطوتان معاً (COMMIT) أو يتم التراجع عنهما معاً (ROLLBACK). لن يكون هناك حالة "مكتملة نصفياً".
🎯 المفاهيم الأساسية
ما هي المعاملة
المعاملة هي سلسلة غير قابلة للتقسيم من عمليات SQL التي إما أن تُنفَّذ بنجاح جميعها أو يتم التراجع عنها جميعها. إنها الآلية الأساسية لضمان اتساق البيانات في قواعد البيانات.
خصائص ACID
| الخاصية | بالإنجليزية | المعنى |
|---|---|---|
| الذرية | Atomicity | جميع العمليات في المعاملة إما أن تنجح أو يتم التراجع عنها |
| الاتساق | Consistency | قاعدة البيانات تنتقل من حالة متسقة إلى أخرى |
| العزل | Isolation | المعاملات المتزامنة لا تتداخل مع بعضها البعض، كما لو أنها تُنفَّذ تسلسلياً |
| الدوام | Durability | بمجرد الالتزام، يتم حفظ البيانات بشكل دائم، حتى لو انهار النظام |
BEGIN / COMMIT / ROLLBACK
-- بدء معاملة
BEGIN; -- أو START TRANSACTION
-- تنفيذ سلسلة من العمليات
UPDATE accounts SET balance = balance - 1000 WHERE id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE id = 2;
-- كل شيء نجح، التزام
COMMIT;
-- حدث خطأ، تراجع
ROLLBACK;
SAVEPOINT — نقاط الحفظ
تعيين "نقطة تحقق" داخل معاملة. يمكنك التراجع إلى نقطة حفظ محددة دون التأثير على العمليات السابقة لها.
BEGIN;
INSERT INTO orders VALUES (1020, 101, 1, '2026-06-28', 500.00, 'pending');
SAVEPOINT sp1;
INSERT INTO order_items VALUES (1020, 1, 2, 100.00);
SAVEPOINT sp2;
-- إذا كانت الخطوة الثانية بها خطأ، تراجع فقط إلى sp1
ROLLBACK TO sp1;
-- الاحتفاظ بعمليات الخطوة الأولى
COMMIT;
| الأمر | الغرض |
|---|---|
SAVEPOINT name |
تعيين نقطة حفظ |
ROLLBACK TO name |
التراجع إلى نقطة الحفظ المحددة (يحتفظ بالعمليات قبل نقطة الحفظ) |
RELEASE SAVEPOINT name |
حذف نقطة الحفظ |
مستويات عزل المعاملات
عندما تعمل عدة معاملات على نفس البيانات في نفس الوقت، قد تحدث المشاكل التالية:
| المشكلة | الوصف | مثال |
|---|---|---|
| قراءة غير نظيفة | قراءة بيانات لم يُلتزم بها بعد من معاملة أخرى | المعاملة A تعدّل بيانات لكنها لم تلتزم؛ المعاملة B تقرأ القيمة المعدّلة |
| قراءة غير قابلة للتكرار | قراءة نفس الصف مرتين في نفس المعاملة تعطي نتائج مختلفة | المعاملة A تقرأ نفس الصف مرتين؛ بينهما، المعاملة B تعدّل ذلك الصف وتلتزم به |
| قراءة شبحية | تنفيذ نفس الاستعلام مرتين في نفس المعاملة يعطي عدد صفوف مختلف | المعاملة A تستعلم مرتين؛ بينهما، المعاملة B تُدرج صفوفاً جديدة |
يُعرّف SQL أربعة مستويات عزل:
| مستوى العزل | قراءة غير نظيفة | قراءة غير قابلة للتكرار | قراءة شبحية | الوصف |
|---|---|---|---|---|
| READ UNCOMMITTED | ✅ ممكن | ✅ ممكن | ✅ ممكن | المستوى الأقل، نادراً ما يُستخدم |
| READ COMMITTED | ❌ لا | ✅ ممكن | ✅ ممكن | الافتراضي في Oracle/PostgreSQL |
| REPEATABLE READ | ❌ لا | ❌ لا | ✅ ممكن | الافتراضي في MySQL |
| SERIALIZABLE | ❌ لا | ❌ لا | ❌ لا | المستوى الأعلى، أسوأ أداء |
-- عرض مستوى العزل الحالي
SELECT @@transaction_isolation;
-- تعيين مستوى العزل
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
الجمود (Deadlocks)
يحدث الجمود عندما تكون معاملتان تنتظران بعضهما البعض لإطلاق الموارد:
المعاملة A: تُقفل الجدول 1 → تنتظر الجدول 2
المعاملة B: تُقفل الجدول 2 → تنتظر الجدول 1
→ انتظار إلى الأبد، لا يمكن إكمال أبداً
كيف تتعامل قاعدة البيانات معه: تكتشف الجمود تلقائياً، تختار معاملة واحدة كـ "ضحية" وتُرجعها، مما يسمح للأخرى بالاستمرار.
استراتيجيات تجنب الجمود:
- الوصول إلى الجداول والصفوف بترتيب ثابت
- تقليل الوقت الذي تحتفظ فيه المعاملات بالأقفال
- استخدام مستويات عزل أقل
- إنشاء فهارس للاستعلامات الشائعة لتقليل نطاق الأقفل
أفضل الممارسات
| المبدأ | الوصف |
|---|---|
| حافظ على المعاملات قصيرة | تقليل وقت الاحتفاظ بالأقفال، تحسين التزامن |
| لا تنتظر مدخلات المستخدم في معاملة | يجب أن تكتمل المعاملات بسرعة |
| استخدم مستويات عزل مناسبة | لا تستخدم المستوى الأعلى كافتراضي |
| استخدم ترتيب وصول ثابت | تقليل احتمالية الجمود |
| أضف معالجة الأخطاء | تراجع فوراً عند حدوث أخطاء |
📝 الأساسيات
-- بدء معاملة
BEGIN; -- MySQL, PostgreSQL
START TRANSACTION; -- مدعوم أيضاً في MySQL
-- الالتزام بالمعاملة (جميع العمليات تأخذ تأثيراً دائماً)
COMMIT;
-- التراجع عن المعاملة (التراجع عن جميع العمليات)
ROLLBACK;
-- نقاط الحفظ
SAVEPOINT savepoint_name;
ROLLBACK TO savepoint_name;
RELEASE SAVEPOINT savepoint_name;
-- تعيين مستوى العزل
SET TRANSACTION ISOLATION LEVEL isolation_level;
-- مستويات العزل: READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE
-- تعيين مستوى العزل العام (يؤثر على جميع الاتصالات الجديدة)
SET GLOBAL TRANSACTION ISOLATION LEVEL isolation_level;
-- مفتاح الالتزام التلقائي (MySQL الافتراضي هو ON)
SET autocommit = 0; -- تعطيل الالتزام التلقائي، يتطلب COMMIT يدوي
SET autocommit = 1; -- تفعيل الالتزام التلقائي (الافتراضي)
- MySQL الافتراضي هو
autocommit = 1، حيث يتم الالتزام التلقائي لكل جملة SQL. قبل استخدام المعاملات، نفّذBEGINأوSET autocommit = 0 - جمل DDL (CREATE، ALTER، DROP) تلتزم ضمنياً بالمعاملة الحالية
- عند قطع الاتصال، يتم التراجع تلقائياً عن المعاملات غير الملزمة
📌 أمثلة
مثال: معاملة إنشاء طلب (طلب + خصم المخزون) (الصعوبة ⭐⭐)
-- السيناريو: عميل يضع طلب منتج؛ يجب إنشاء الطلب وخصم المخزون في نفس الوقت
BEGIN;
-- الخطوة 1: إنشاء الطلب
INSERT INTO orders (order_id, customer_id, employee_id, order_date, total_amount, status)
VALUES (1020, 101, 1, '2026-06-28', 6999.00, 'pending');
-- الخطوة 2: خصم المخزون
UPDATE products SET stock = stock - 1 WHERE product_id = 1;
-- الخطوة 3: التحقق من كفاية المخزون (تراجع إذا كان المخزون < 0)
-- عملياً، يتم التعامل مع هذا عادةً بواسطة منطق التطبيق أو المُحفِّزات
-- كل شيء نجح، التزام
COMMIT;
إذا حدث خطأ في منتصف الطريق:
BEGIN;
INSERT INTO orders (order_id, customer_id, employee_id, order_date, total_amount, status)
VALUES (1021, 102, 3, '2026-06-28', 129.00, 'pending');
UPDATE products SET stock = stock - 1 WHERE product_id = 2;
-- افترض أننا اكتشفنا عدم كفاية المخزون، تراجع عن المعاملة بالكامل
ROLLBACK;
-- كل من الطلب والمخزون يعودان إلى حالتهما قبل المعاملة
مثال: استخدام SAVEPOINT للتراجع الجزئي (الصعوبة ⭐⭐⭐)
BEGIN;
-- الطلب الأول: ناجح
INSERT INTO orders VALUES (1022, 103, 4, '2026-06-28', 3500.00, 'pending');
UPDATE products SET stock = stock - 1 WHERE product_id = 4;
SAVEPOINT after_first_order;
-- الطلب الثاني: خطأ
INSERT INTO orders VALUES (1023, 104, 4, '2026-06-28', 2200.00, 'pending');
UPDATE products SET stock = stock - 1 WHERE product_id = 5;
SAVEPOINT after_second_order;
-- اكتشفنا مشكلة في الطلب الثاني، تراجع إلى ما بعد الأول
ROLLBACK TO after_first_order;
-- الطلب الأول لا يزال صالحاً، التزام
COMMIT;
-- النتيجة: الطلب 1022 محفوظ، الطلب 1023 تم التراجع عنه
التحقق:
SELECT order_id, total_amount, status FROM orders WHERE order_id >= 1022;
order_id | total_amount | status
---------+-------------+--------
1022 | 3500.00 | pending
الشرح: ROLLBACK TO after_first_order يلغي فقط العمليات بعد نقطة الحفظ. الطلب الأول وخصم المخزون محفوظان.
🎬 تدريب على السيناريوهات
السيناريو 1: تعديل رواتب الموظفين دفعة واحدة (مع معالجة الأخطاء)
BEGIN;
-- رفع راتب قسم المبيعات بنسبة 10%
UPDATE employees
SET salary = salary * 1.10
WHERE department_id = (SELECT department_id FROM departments WHERE department_name = 'Sales');
SAVEPOINT after_sales_raise;
-- رفع راتب قسم التكنولوجيا بنسبة 8%
UPDATE employees
SET salary = salary * 1.08
WHERE department_id = (SELECT department_id FROM departments WHERE department_name = 'Tech');
SAVEPOINT after_tech_raise;
-- افترض أن موافقة الميزانية لم تمر، فقط التراجع عن رفع راتب التكنولوجيا
ROLLBACK TO after_sales_raise;
-- رفع راتب المبيعات محفوظ، التزام
COMMIT;
النقطة الأساسية: SAVEPOINT يُمكّن "التراجع الجزئي"، مناسب للعمليات الدفعية التي تتطلب تحكماً مرناً.
السيناريو 2: محاكاة تحويل والتعامل مع الجمود
-- الجلسة A: تحويل ميزانية من القسم 1 إلى القسم 2
BEGIN;
UPDATE departments SET budget = budget - 50000 WHERE department_id = 1;
-- انتظر لحظة...
UPDATE departments SET budget = budget + 50000 WHERE department_id = 2;
COMMIT;
-- الجلسة B: في نفس الوقت تحويل ميزانية من القسم 2 إلى القسم 1 (اتصال آخر)
BEGIN;
UPDATE departments SET budget = budget - 30000 WHERE department_id = 2;
UPDATE departments SET budget = budget + 30000 WHERE department_id = 1;
COMMIT;
منع الجمود:
-- كلا الجلستين تصلان إلى الأقسام بترتيب تصاعدي حسب department_id
-- الجلسة A:
BEGIN;
UPDATE departments SET budget = budget - 50000 WHERE department_id = 1; -- قفل 1 أولاً
UPDATE departments SET budget = budget + 50000 WHERE department_id = 2; -- ثم قفل 2
COMMIT;
-- الجلسة B:
BEGIN;
UPDATE departments SET budget = budget + 30000 WHERE department_id = 1; -- أيضاً قفل 1 أولاً
UPDATE departments SET budget = budget - 30000 WHERE department_id = 2; -- ثم قفل 2
COMMIT;
النقطة الأساسية: استخدام ترتيب وصول ثابت هو أبسط وأكثر طريقة فعالية لتجنب الجمود.
❓ أسئلة شائعة
س: MySQL افتراضياً يلتزم تلقائياً — كيف أستخدم المعاملات؟ ج: MySQL الافتراضي هو
autocommit = 1، حيث يتم الالتزام التلقائي لكل جملة SQL. لاستخدام المعاملات، نفّذBEGIN(أوSTART TRANSACTION) أولاً؛ الجمل SQL اللاحقة لن يتم الالتزام بها تلقائياً حتى تنفّذCOMMITأوROLLBACK.
س: هل يمكنني التراجع بعد الالتزام؟ ج: لا.
COMMITهو نهاية المعاملة؛ بمجرد الالتزام، يتم حفظ البيانات بشكل دائم ولا يمكن التراجع عنها. "للتراجع" عن بيانات ملزمة، يجب تنفيذ عملية عكسية في معاملة جديدة (مثل: UPDATE للقيمة الأصلية).
س: أي مستوى عزل يجب أن أختار؟ ج: لمعظم السيناريوهات، استخدم الافتراضي في قاعدة البيانات (MySQL الافتراضي
REPEATABLE READ، Oracle/PostgreSQL الافتراضيREAD COMMITTED). استخدمSERIALIZABLEفقط عندما تحتاج صراحةً إلى منع القراءات الشبحية وتقبل تكلفة الأداء.
س: هل جمل DDL (CREATE TABLE، ALTER TABLE) تتحكم بها المعاملات؟ ج: لا. جمل DDL تلتزم ضمنياً بالمعاملة الحالية. بعد تنفيذ DDL في معاملة، لا يمكن التراجع عن جمل SQL السابقة. هذا سلوك MySQL؛ في PostgreSQL، يمكن التراجع عن DDL.
📖 ملخص
| المفهوم | الوصف |
|---|---|
| المعاملة | مجموعة من عمليات قاعدة البيانات غير القابلة للتقسيم |
| ACID | الذرية، الاتساق، العزل، الدوام |
| BEGIN | بدء معاملة |
| COMMIT | الالتزام بالمعاملة، جعلها دائمة |
| ROLLBACK | التراجع عن المعاملة، التراجع عن كل شيء |
| SAVEPOINT | نقطة تحقق داخل معاملة، تدعم التراجع الجزئي |
| مستوى العزل | يتحكم في الرؤية بين المعاملات المتزامنة |
| الجمود | معاملتان تنتظران بعضهما البعض؛ قاعدة البيانات تكتشف تلقائياً وتُرجع إحداهما |
- حافظ على المعاملات قصيرة لتقليل وقت الاحتفاظ بالأقفال
- خذ الموارد بترتيب ثابت لتقليل الجمود
- اختر مستوى العزل المناسب بناءً على احتياجات العمل
📝 تمارين
- اكتب معاملة تُدرج طلباً جديداً في جدول
ordersمع خصم المخزون المقابل من جدولproducts. إذا كان المخزون غير كافٍ، تراجع عن المعاملة بالكامل. - استخدم
SAVEPOINTلتنفيذ معاملة تُدرج 3 سجلات طلبات، ثم تراجع عن الثالث مع الاحتفاظ بالأولين. - تحقق من مستوى العزل الافتراضي في قاعدة البيانات الخاصة بك وجرّب تعيينه إلى
READ COMMITTEDلملاحظة التأثير على القراءات والكتابات المتزامنة. - سؤال تفكير: إذا استغرقت معاملة وقتاً طويلاً للتنفيذ، ما تأثيرها على المعاملات الأخرى؟ كيف يمكن تحسين ذلك?
الدرس التالي
👉 22-stored-procedures - الإجراءات المخزنة: تعلّم إنشاء واستدعاء الإجراءات المخزنة، المُعامِلات المدخلة/المخرَجة، جمل التحكم في التدفق، والتطبيقات العملية للإجراءات المخزنة.



