SQLite / المتوسط: بنية الاستعلام وتصميم المعاملة / الدرس 8
المعاملات ونقاط الحفظ ونتائج الأخطاء
اجمع تغييرات SQLite المرتبطة في نتيجة واحدة، وعالج فشل العبارة وحالة المعاملة والتراجع إلى نقطة حفظ وسياسة الاتصال صراحة.
في هذه الصفحة
أهداف الدرس
- تحديد مالك المعاملة وحدود حفظها.
- استخدام نقاط الحفظ بدلاً من BEGIN متداخلة.
- تمييز رفض عبارة عن التراجع عن المعاملة كلها.
اختر وحدة العمل التي يجب أن تنجح معاً
يجب أن يحفظ خصم التحويل وإضافته معاً أو ألا يبقى أي منهما. لا تحقق عبارتان منفصلتان ناجحتان في وضع الحفظ التلقائي هذه الذرية التجارية. تبدأ BEGIN وحدة صريحة، وتقبلها COMMIT، وتلغيها ROLLBACK. تنشئ SQLite أيضاً معاملات تلقائية حول عمل القاعدة عندما لا توجد معاملة صريحة، ويرتبط اكتمالها بدورة حياة العبارة.
اجعل المعاملة قصيرة وأبعد تفاعل المستخدم واستدعاءات الخدمات البعيدة أثناء الاحتفاظ بموارد القاعدة. لا يعكس التراجع رسالة بريد أرسلت أو دفعة خارجية اكتملت. يحدد المالك النتائج المقبولة وما يحدث عند فقد الاتصال قرب الحفظ، حين قد يجهل التطبيق هل أصبح التغيير دائماً. تضيف أعمال التكامل اللاحقة هوية طلب دائمة للمصالحة.
لاحظ التراجع على جداول قاعدة عادية
ينشئ البرنامج الأول حسابين ويمولهما قبل معاملة التحويل، ثم ينفذ التغييرين ويتراجع عنهما عمداً. يظل المبلغان النهائيان100 و50. يبين ذلك عكس انتقال الحالة المعروض في القاعدة المعزولة، ولا يحاكي انقطاع الطاقة أو تلف التخزين أو كل افتراضات المعاملات في مشغل البيئة المضيفة.
تبقى المفاتيح الخارجية والقيود الأخرى حمايات منفصلة. قد تحفظ المعاملة تحويلاً خاطئاً حسابياً بصورة ذرية إذا أخطأت الشروط أو المبالغ. افحص عدد الصفوف المتوقع تأثرها وتحقق من المدخلات التجارية. لا يعد تحديث صفر صفوف خطأ SQL بالضرورة، لذلك قد يحتاج التطبيق المضيف إلى رفض العملية كلها صراحة عند غياب حساب مطلوب.
CREATE TABLE account(id INTEGER PRIMARY KEY,amount INTEGER NOT NULL);
INSERT INTO account VALUES(1,100),(2,50);
BEGIN;
UPDATE account SET amount=amount-10 WHERE id=1;
UPDATE account SET amount=amount+10 WHERE id=2;
ROLLBACK;
SELECT id,amount FROM account ORDER BY id;
id|amount
1|100
2|50
يعكس التراجع التحديثين فتبقى أرصدة ما قبل المعاملة100 و50. تبقى إضافات التجهيز لأنها سبقت BEGIN.
استخدم نقاط الحفظ لحد تراجع داخلي
لا تتيح SQLite تداخل معاملات BEGIN. تسمي SAVEPOINT موضعاً في مكدس المعاملات، وتعيد ROLLBACK TO التغييرات اللاحقة له دون إنهاء المعاملة الخارجية، وتزيل RELEASE الحد المسمى. تحرير نقطة حفظ داخلية ليس حفظاً دائماً مستقلاً؛ فما زال التراجع الخارجي قادراً على عكس تغييراتها. وثق الملكية عندما تشارك دالة مساعدة في عمل يملكه المستدعي.
يبقي البرنامج الثاني الخصم الأول بعشر وحدات، ويتخلى عن تعديل إضافي بخمس. تترك ROLLBACK TO نقطة الحفظ نشطة، لذلك تكمل RELEASE ذلك الحد قبل COMMIT. يصبح المبلغ النهائي90. يناسب النمط سياسة تسمح بالتعافي الجزئي فقط؛ فلا ينبغي لتحويل يلزم كل خطواته أن يحتفظ بأحد طرفيه بصمت.
CREATE TABLE account(id INTEGER PRIMARY KEY,amount INTEGER NOT NULL);
INSERT INTO account VALUES(1,100);
BEGIN;
UPDATE account SET amount=amount-10 WHERE id=1;
SAVEPOINT adjustment;
UPDATE account SET amount=amount-5 WHERE id=1;
ROLLBACK TO adjustment;
RELEASE adjustment;
COMMIT;
SELECT amount FROM account WHERE id=1;
amount
90
يبقى تحديث العشر وحدات، ويعكس التعديل بخمس وحدات قبل حفظ المعاملة الخارجية.
لا يعني رفض العبارة دائماً تراجعاً كاملاً
تختلف آثار خوارزميات التعارض ABORT وFAIL وIGNORE وREPLACE وROLLBACK، ولا تنطبق بالطريقة نفسها على كل قيد. في سلوك ABORT المعتاد، تتراجع العبارة الفاشلة بينما قد يبقى العمل السابق داخل المعاملة نشطاً. لا تلتقط خطأ قيد ثم تفترض أن العملية التجارية كلها زالت تلقائياً.
تحتاج أخطاء الإدخال والإخراج والذاكرة وامتلاء القرص والانشغال المعالجة الموثقة لكل منها. على البيئة المضيفة فحص حالة المعاملة وتنظيفها وفق الحد المملوك مع حفظ الخطأ الأصلي. قد تترك COMMIT المنشغلة المعاملة نشطة وتسمح بإعادة الحفظ، بينما تحتاج لقطة WAL المتقادمة استراتيجية أخرى. تفيد الرموز والسياق أكثر من إعادة عامة لكل استثناء.
طابق افتراضات الاتصال مع نية المعاملة
تؤخر BEGIN DEFERRED بعض الحجز حتى يحدث العمل، بينما تحاول BEGIN IMMEDIATE بدء معاملة الكتابة مقدماً. لا يكتب إلا اتصال واحد في الوقت نفسه، ولا تجعل IMMEDIATE السرعة أو الصحة أفضل دائماً. يختلف سلوك EXCLUSIVE بحسب نمط اليومية. اختر السياسة من التنافس وتسلسل القراءة والكتابة ومعنى العملية، لا من نسخ أمر قفل.
قد تضيف API المضيفة معاملات ضمنية. أدخل Python3.12 واجهة autocommit إلى جانب معالجة isolation_level القديمة؛ وتتجنب الأمثلة المضبوطة صراحة خلط النموذجين. اختبر الحفظ العادي والرفض بعد التغيير الأول والتعافي بنقطة حفظ وتنظيف الاتصال. تعتمد المتانة أيضاً على اليومية والمزامنة وضمانات التخزين؛ وليس تعطيل هذه الحمايات تحسين أداء بلا عواقب.
الأخطاء الشائعة وإصلاحها
cannot start a transaction within a transaction
- لماذا يحدث؟
- أُصدرت BEGIN صريحة داخل عمل معاملاتي موجود.
- كيف تصلحه؟
- استخدم نقطة حفظ أو احترم عقد المعاملة التي يملكها المستدعي.
- كيف تتجنب تكراره؟
- افحص إعدادات المعاملات الضمنية في البيئة المضيفة.
بقيت تغييرات سابقة بعد خطأ قيد.
- لماذا يحدث؟
- اختلط تراجع العبارة بالتراجع عن المعاملة.
- كيف تصلحه؟
- ليتراجع المالك عن العملية الفاشلة كاملة عندما يلزم ذلك.
- كيف تتجنب تكراره؟
- اختبر حدوث فشل بين التغييرات المرتبطة.
اختفى تغيير داخلي محرر لاحقاً.
- لماذا يحدث؟
- اعتُبرت RELEASE حفظاً مستقلاً داخل معاملة خارجية.
- كيف تصلحه؟
- وثق أن المعاملة الخارجية تتحكم في المتانة النهائية.
- كيف تتجنب تكراره؟
- اختبر استدعاء الدوال المساعدة والتراجع الخارجي.
اختبر فهمك
1. يغير الخصم صفاً واحداً ولا تغير الإضافة المطلوبة أي صف. هل ينبغي حفظ التحويل؟
اعرض الحل المشروح
لا، إذا كان الطرفان مطلوبين في وحدة العمل. اكتشف غياب الأثر اللازم وتراجع عبر الحد الذي تملكه البيئة المضيفة. نجاح عبارة تحديث صفر صفوف من حيث SQL لا يرفع تلقائياً الفشل التجاري المطلوب.
2. هل تحمي RELEASE لنقطة حفظ داخلية تغييراتها من ROLLBACK خارجية لاحقة؟
اعرض الحل المشروح
لا. تزيل الحد الداخلي ولا تجعل تغييره دائماً مستقلاً. تظل المعاملة الخارجية مالكة قرار الحفظ أو التراجع عن الكل. استخدم نقطة الحفظ فقط عندما تتفق دلالة التعافي الجزئي مع العملية.
أهم ما تعلمته
- تتبع الذرية حد العمل التجاري المعلن.
- نقاط الحفظ مواضع تراجع داخلية وليست عمليات حفظ مستقلة.
- تحدد حالة الخطأ وAPI المضيفة التنظيف أو الإعادة الصحيحين.