دروس الوحدة (3/4)
القفل والاختيار للتحديث
إن فهم كيفية عمل قاعدة البيانات بشكل منفصل على محطة واحدة هو أمر بسيط... لا تنتظر، لقد قلت ذلك بالفعل!
في بعض الأحيان لا تكون "التحديثات الذرية" (على سبيل المثال stock = stock - 1) كافية.
لنتخيل أنه يتعين عليك التحقق مما إذا كان لدى المستخدم رصيد كافٍ لشراء أشياء بقيمة 50 يورو:
- محمي_INLINE_1
- JS: إذا كان الرصيد أقل من 50، قم بحظره!
- وإلا تابع عملية الدفع...
- قم بإجراء الخصم! (وهنا تجد مستخدمًا، أثناء التحقق من صحة الشيك في الخطوة 2، أطلق شيكًا موازيًا آخر احترق في حسابه والآن أنت تنخفض إلى ما دون الصفر!).
لتحديث القفل الصريح
تؤدي إضافة FOR UPDATE في نهاية SELECT إلى تحويل قراءتك البريئة بطريقة سحرية إلى قفل مغلق ضد العملاء الآخرين حتى الالتزام التالي. لن يتمكن أي شخص آخر من إجراء التحديث (أو القراءة المتزامنة للتحديث) على تلك الصفوف المحددة المحددة حتى تقرر ما ستفعله بها!
BEGIN;
-- If id=3 had already been locked by T1, T2 will sit in perpetual loading on this "read"
-- frozen in place, before receiving the raw data, until T1 runs COMMIT freeing everyone!
SELECT balance
FROM users
WHERE id = 3
FOR UPDATE;
-- at this point our Node JS instance lives with the guarantee, knowing for certain that NO ONE ELSE in the world has
-- manipulated (nor been able to read with intent to alter) that balance while we complete the application logic
UPDATE users SET balance = balance - 50 WHERE id = 3;
COMMIT;هناك أيضًا FOR SHARE الذي يسمح للقراء الآخرين بمواصلة القراءة (إذا كانوا على قفل أساسي) دون حظر قراءة الحالة، ولكن مع حظر أي تحديث لاحق محتمل حتى تلتزم. ومع ذلك، بالنسبة للتعديلات، استخدم دائمًا القوة الغاشمة لجملة القفل الحصرية:
جربه بنفسك
افتح معاملة عادية ذات نطاق زمني وقم بتأمين قراءة معرف المنتج=5 بشكل حصري.\nقم بتشغيل استعلام من خطوتين:\n1. افتح المعاملة (البدء)\n2. استعلم عن "المنتجات" التي تجلب الصف الكامل للمعرف 5 مع ضمان عدم قيام أي شخص بتغييره، وذلك عن طريق إرفاق خيار "للتحديث".
إظهار التلميح
البدء؛ السطر الجديد، حدد * من المنتجات حيث المعرف = 5 للتحديث؛
الحل متاح بعد 3 من المحاولات
في بعض الأحيان، تتوقف المعاملة لفترات كارثية (حالة توقف تام أو انتظار لا نهائي).\nلمنع ذلك، يمكنك أن تأمر قاعدة البيانات بالتخلي عن اللحظة التي لا يكون فيها الصف متاحًا على الفور: استخدام NOWAIT في النهاية، مما قد يتسبب في عودة استثناء خادم SQL إلى JS لتتم محاولته دون تعليقه إلى الأبد.\nقم بتشغيل SELECT لمعرف الفئة=1 واستخدم FOR UPDATE مع فرض NOWAIT.
إظهار التلميح
اختر * من الفئات حيث المعرف = 1 للتحديث الآن؛
الحل متاح بعد 3 من المحاولات