Kodokon kodokon.com

التزامن: الأقفال، ومستويات العزل، والقراءات الشبحية

افهم نموذج القفل في SQLite، ومعاملاتها، وشذوذات العزل التي يصفها معيار SQL.

12 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

تقفل SQLite على مستوى ملف قاعدة البيانات بأكمله، لا على مستوى الصف. في الوضع الافتراضي (سجل التراجع rollback journal)، يتشارك القارئون قفلًا SHARED، لكن كاتبًا واحدًا فقط في كل مرة يستطيع الحصول على القفل EXCLUSIVE. والنتيجة: تُسلسَل عمليات الكتابة. لننشئ جدولًا للعمل مع المعاملات.

SQL
CREATE TABLE accounts (
  id INTEGER PRIMARY KEY,
  balance INTEGER NOT NULL
);

INSERT INTO accounts (id, balance)
VALUES (1, 100), (2, 50);

-- Read: deferred transaction (default)
BEGIN;
SELECT balance FROM accounts WHERE id = 1;
COMMIT;
معاملة صريحة مُحاطة بـ BEGIN/COMMIT.

يتطوّر القفل على مراحل: UNLOCKED، ثم SHARED (قراءة)، وRESERVED (نية الكتابة)، وPENDING، وأخيرًا EXCLUSIVE (كتابة). فإذا كانت معاملة تحمل بالفعل قفل الكتابة، تتلقّى أخرى الخطأ SQLITE_BUSY. وبدلًا من الفشل فورًا، حدِّد مهلة انتظار باستخدام busy_timeout، وفعِّل وضع WAL للسماح للقارئين والكاتب بالتقدّم على التوازي.

SQL
-- A single writer, concurrent readers
PRAGMA journal_mode = WAL;

-- Wait up to 5 s if the database is busy
PRAGMA busy_timeout = 5000;
WAL يفكّ حجب القراءات المتزامنة؛ وbusy_timeout ينتظر.

يصف معيار SQL ثلاثة شذوذات: القراءة القذرة (قراءة بيانات غير مُثبَّتة)، والقراءة غير القابلة للتكرار (إعادة قراءة صف ورؤية قيمة مختلفة)، والقراءة الشبحية (إعادة تنفيذ استعلام على نطاق ورؤية صفوف جديدة تظهر). ولأن SQLite تُسلسِل الكُتّاب، فإنها تتصرّف فعليًّا كأنها SERIALIZABLE: فلا تظهر هذه الشذوذات (خارج وضع الذاكرة المؤقتة المشتركة مع read_uncommitted). وفي غير ذلك، تختار المستوى صراحةً.

SQL
-- Atomic transfer: immediate write lock
BEGIN IMMEDIATE;
UPDATE accounts
SET balance = balance - 10
WHERE id = 1;
UPDATE accounts
SET balance = balance + 10
WHERE id = 2;
COMMIT;
BEGIN IMMEDIATE يحجز القفل قبل أيّ كتابة.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. أيّ مستوى عزل تقدّمه SQLite عمليًّا؟
    • READ UNCOMMITTED، القراءات القذرة ممكنة افتراضيًّا
    • READ COMMITTED، مثل PostgreSQL
    • SERIALIZABLE، لأن كاتبًا واحدًا يعمل في كل مرة
    • لا شيء، إذ لا توجد معاملات
  2. ما الفرق بين BEGIN وBEGIN IMMEDIATE؟
    • BEGIN IMMEDIATE يأخذ قفل الكتابة فورًا؛ أما BEGIN فينتظر أول وصول للكتابة
    • BEGIN IMMEDIATE يعطّل سجل التراجع
    • BEGIN IMMEDIATE ينشئ قاعدة بيانات جديدة
    • لا فرق في السلوك
  3. ما هي القراءة الشبحية؟
    • قراءة قيمة لم تُثبِّتها معاملة أخرى بعد
    • إعادة قراءة الصف نفسه والحصول على قيمة مختلفة
    • إعادة تنفيذ استعلام على نطاق ورؤية صفوف جديدة أُدرِجت في هذه الأثناء تظهر
    • قراءة صف حُذِف للتو