Menu
flag Ar iconالعربيةdown icon
جرّب في Playground

قاعدة بيانات SQLite في الذاكرة باستخدام :memory:

كيف تعمل قاعدة بيانات SQLite في الذاكرة، ومتى تستخدم :memory:، وما الفرق بينها وبين قاعدة بيانات محفوظة في ملف على القرص.

تحتوي هذه الصفحة على محررات قابلة للتشغيل — حرّر، شغّل، وشاهد النتيجة فوراً.

قاعدة بيانات تعيش داخل الذاكرة

يُتيح لك SQLite اسمًا خاصًا للملف هو :memory:. عند فتح قاعدة بيانات بهذا الاسم، يتجاوز SQLite القرص الصلب تمامًا، وتعمل قاعدة البيانات بالكامل داخل الذاكرة (RAM). الجداول، الفهارس، المعاملات، المفاتيح الأجنبية، وكل ميزة أخرى تعمل بنفس الطريقة تمامًا. الفارق الوحيد أنه بمجرد إغلاق الاتصال، تختفي القاعدة بالكامل.

من سطر الأوامر:

sqlite3 :memory:

أنت الآن أمام موجّه SQLite، ومعك قاعدة بيانات فارغة جديدة موجودة في الذاكرة فقط. أنشئ جدولاً، وأدخِل بعض الصفوف، ثم استعلم عنها — كل شيء يسير بشكل طبيعي:

أغلِق الجلسة وستتبخّر البيانات. لن يتبقّى أي ملف على القرص، ببساطة لأنه لم يُنشأ ملف من الأساس.

لماذا قد تحتاج قاعدة بيانات SQLite في الذاكرة؟

قاعدة بيانات لا تصمد بعد إعادة التشغيل تبدو وكأنها خلل لا ميزة. لكنها في الواقع مفيدة جداً في ثلاث حالات.

الاختبارات. كل اختبار يحصل على قاعدة بيانات نظيفة خلال أجزاء من الثانية. لا ملفات مؤقتة تحتاج إلى تنظيف، ولا حالة متبقية من تشغيلة سابقة، ولا ملف fixture مشترك يقع في فخ القفل. معظم منظومات الاختبار في Python و Node و Go التي تعتمد على SQLite تفتح :memory: لهذا السبب بالذات.

التحليل السريع العابر. حمّل ملف CSV، نفّذ بعض الاستعلامات، ثم تخلّص منه. أسرع من تشغيل قاعدة بيانات حقيقية، وأسهل من تحليل الملف برمجياً في كل مرة.

التخزين المؤقت ومساحة العمل. داخل برنامج يعمل لفترة طويلة، تُعدّ قاعدة بيانات SQLite في الذاكرة محرّك استعلامات مرتجلاً ممتازاً للبيانات التي حمّلتها مسبقاً.

القاسم المشترك بين هذه الحالات: تريد SQL، لكن لا تريد الاستمرارية.

الأداء: أسرع، لكن ليس بطريقة سحرية

قواعد البيانات في الذاكرة تتجاوز القرص، فعمليات الكتابة التي كانت ستذهب إلى نظام الملفات تتحوّل إلى مجرد تحديثات في الذاكرة. الأحمال المقيّدة بالإدخال/الإخراج (I/O-bound) تتحسّن بشكل ملحوظ. أما الأحمال المقيّدة بالمعالج (CPU-bound) — كتخطيط الاستعلامات المعقّدة وعمليات الفرز الضخمة — فلن تلاحظ عليها فرقاً يُذكر، لأن SQLite يحتفظ أصلاً بالصفحات الساخنة في الذاكرة.

وإليك مثالاً سريعاً يوضّح كم أن البنية متطابقة تماماً:

ذلك المثال اشتغل على قاعدة بيانات في الذاكرة، لكنه نفس الـ SQL الذي ستكتبه لو كانت القاعدة على شكل ملف. محرّك قاعدة البيانات لا يفرّق بين الحالتين.

الفرق بين sqlite في الذاكرة وملف: متى تستخدم كل واحدة؟

المقارنة بسيطة، ويستحق الأمر توضيحها بشكل صريح:

  • قاعدة بيانات على شكل ملف (mydata.db): تبقى محفوظة بعد إعادة التشغيل. يمكن لعدة عمليات أن تفتحها معًا. وتصمد أمام الأعطال (مع وضع WAL، في الغالب). استخدمها لأي شيء يحتاج أن يتذكّر بياناته.
  • قاعدة بيانات في الذاكرة (:memory:): تختفي بمجرد إغلاق الاتصال. وتكون خاصة بالاتصال الذي فتحها (افتراضيًا). أسرع في العمليات كثيفة الكتابة المؤقتة. استخدمها للاختبارات، والتجارب السريعة، والكاش قصير العمر.

إذا كنت متردّدًا، اختر الملف. أما الذاكرة فهي الحالة الاستثنائية.

كل اتصال يحصل على قاعدة بيانات خاصة به

نقطة دقيقة كثيرًا ما توقع المبتدئين: فتح :memory: مرتين يعطيك قاعدتي بيانات منفصلتين تمامًا. لا تتشاركان الجداول، ولا البيانات، ولا حتى تعرف إحداهما بوجود الأخرى.

-- الطرفية 1
sqlite3 :memory:
sqlite> CREATE TABLE t (x); INSERT INTO t VALUES (1);

-- الطرفية 2
sqlite3 :memory:
sqlite> SELECT * FROM t;
Error: no such table: t

هذه ليست مشكلة في SQLite، بل هي طريقة عمله المقصودة. عندما تكتب :memory: فأنت تطلب من SQLite إنشاء "قاعدة بيانات خاصة بهذا الاتصال فقط". وينطبق الأمر نفسه داخل البرنامج الواحد: إذا فتح الكود اتصالَين باستخدام :memory:، فسيحصل كل اتصال على قاعدة بياناته المعزولة تمامًا عن الآخر.

مشاركة قاعدة بيانات SQLite في الذاكرة بين عدة اتصالات

إذا احتجت فعلًا إلى أن ترى عدة اتصالات نفس قاعدة البيانات الموجودة في الذاكرة، فإن SQLite يدعم ذلك عن طريق أسماء الملفات بصيغة URI مع تفعيل الكاش المشترك (shared cache). والسلسلة السحرية هنا هي file::memory:?cache=shared:

sqlite3 'file::memory:?cache=shared'

أيّ اتصال داخل نفس العملية يفتح هذا الـ URI بالتحديد سينضمّ تلقائيًا إلى قاعدة البيانات نفسها. وبمجرّد إغلاق كل الاتصالات، تختفي القاعدة بالكامل.

كذلك تستطيع إنشاء قاعدة بيانات في الذاكرة باسم محدّد، وهذا مفيد عندما تحتاج إلى عدّة قواعد بيانات مشتركة ومنفصلة عن بعضها:

sqlite3 'file:mydb?mode=memory&cache=shared'

الاسم mydb هنا مجرد تسمية تعريفية — لا يوجد ملف فعلي. أي اتصالين يفتحان file:mydb?mode=memory&cache=shared سيتشاركان نفس قاعدة البيانات، بينما الاتصال الذي يفتح file:other?mode=memory&cache=shared سيحصل على قاعدة بيانات مختلفة تمامًا.

حفظ قاعدة بيانات SQLite من الذاكرة إلى القرص

في بعض الأحيان تنفّذ سير عمل كاملًا داخل الذاكرة، ثم تقرر أنك تريد الاحتفاظ بالنتيجة. واجهة الأوامر CLI توفّر لك أمر النقطة .backup لهذا الغرض:

sqlite3 :memory:
sqlite> CREATE TABLE results (id INTEGER, score REAL);
sqlite> INSERT INTO results VALUES (1, 0.91), (2, 0.87);
sqlite> .backup snapshot.db
sqlite> .quit

snapshot.db صار الآن قاعدة بيانات عادية على هيئة ملف، وتحتوي على نفس البيانات. تقدر تفتحها لاحقًا بالأمر sqlite3 snapshot.db وتكمل من حيث وقفت.

والعملية العكسية ممكنة كذلك؛ فالأمر .restore يقوم بتحميل قاعدة بيانات من ملف إلى الذاكرة الخاصة بالاتصال الحالي:

sqlite3 :memory:
sqlite> .restore snapshot.db
sqlite> SELECT * FROM results;

من داخل كود التطبيق، تُتيح واجهة SQLite C API نفس الآلية المتوفرة في sqlite3_backup_init، ومعظم روابط اللغات (language bindings) تغلّفها بشكل جاهز. على سبيل المثال، توفّر وحدة sqlite3 في بايثون الدالة Connection.backup().

فخ شائع يقع فيه الكثيرون

يحاول البعض "حفظ" قاعدة بيانات موجودة في الذاكرة عن طريق إرفاق ملف ثم نسخ المحتوى إليه على هذا النحو:

هذا يعمل مع نسخ الجداول البسيطة، لكنه لا يحافظ على الفهارس والمشغّلات (triggers) والعروض (views) والمفاتيح الأجنبية كما كانت بالضبط. إذا أردت نسخة طبق الأصل من قاعدة بيانات كاملة، استخدم .backup (أو واجهة backup API) — فهي تنسخ نسخة ثنائية دقيقة على مستوى الصفحات.

خلاصة الدرس

  • :memory: هو اسم ملف خاص في SQLite ينشئ قاعدة بيانات داخل الذاكرة (RAM) بدون أي ملف على القرص.
  • صياغة SQL مطابقة تماماً لقاعدة بيانات على شكل ملف — نفس الجداول، ونفس الاستعلامات، ونفس القيود.
  • كل اتصال بـ :memory: يكون خاصاً ومعزولاً؛ استخدم روابط الذاكرة المشتركة (file::memory:?cache=shared) عندما تحتاج عدة اتصالات أن تشترك في نفس قاعدة البيانات.
  • إنها الأداة المناسبة للاختبارات، والتحليلات المؤقتة، والذواكر المخبّأة قصيرة العمر — وليست مناسبة لأي شيء يجب أن يبقى بعد إعادة التشغيل.
  • يمكنك ترقية قاعدة البيانات من الذاكرة إلى القرص باستخدام .backup عندما تقرر أنك تريد الاحتفاظ بها.

التالي: إنشاء الجداول

شاهدت CREATE TABLE يمر سريعاً في بعض الأمثلة السابقة. الصفحة القادمة ستتمهّل قليلاً وتشرحه كما يجب — تعريف الأعمدة، والأنواع، والقيود التي يمكن إلحاقها، والخيارات الصغيرة التي تجعل تصميم المخطط (schema) مريحاً للعمل معه.

الأسئلة الشائعة

كيف تنشئ قاعدة بيانات SQLite في الذاكرة؟

افتح SQLite باستخدام الاسم الخاص :memory: بدلاً من مسار ملف. من سطر الأوامر تكتب sqlite3 :memory:، ومن أي مكتبة برمجية تمرّر :memory: كاسم ملف لدالة الاتصال. ستعيش قاعدة البيانات في الذاكرة العشوائية (RAM) وتختفي بمجرد إغلاق الاتصال.

ما هو :memory: في SQLite؟

:memory: اسم ملف خاص يفهمه SQLite على أنه: «لا تستخدم ملفاً، احتفظ بكل شيء في الذاكرة». تحصل على قاعدة بيانات SQLite كاملة الميزات — جداول، فهارس، transactions، كل شيء — لكن دون أي كتابة على القرص. ولاحظ أن كل اتصال يفتح :memory: يحصل على قاعدة بيانات خاصة به ومعزولة عن غيره.

هل يمكن لاتصالين مشاركة نفس قاعدة البيانات في الذاكرة؟

افتراضياً لا، فكل اتصال بـ :memory: معزول تماماً. لمشاركتها استخدم رابط URI بهذا الشكل file::memory:?cache=shared مع تفعيل الـ shared cache. عندها كل اتصال يفتح نفس الـ URI داخل العملية (process) نفسها سيرى القاعدة ذاتها.

هل يمكن حفظ قاعدة بيانات SQLite في الذاكرة على القرص؟

نعم. استخدم أمر .backup من سطر الأوامر، أو واجهة backup API في مكتبتك لنسخ القاعدة من الذاكرة إلى ملف. يمكنك أيضاً استخدام ATTACH لربط ملف قاعدة بيانات، ثم تنفيذ INSERT INTO file.table SELECT * FROM main.table لنقل البيانات.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن