Menu

SQLite In-Memory Database: מסדי נתונים מהירים וזמניים עם :memory:

איך עובד מסד נתונים בזיכרון ב-SQLite, מתי כדאי להשתמש ב-:memory:, ובמה הוא שונה ממסד נתונים מבוסס קובץ שאפשר לשמור.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

מסד נתונים שחי ב-RAM

ל-SQLite יש שם קובץ מיוחד: :memory:. פתחו מסד נתונים בשם הזה ו-SQLite מדלגת לגמרי על הדיסק: כל מסד הנתונים חי ב-RAM. טבלאות, אינדקסים, טרנזקציות, מפתחות זרים, כל תכונה עובדת בדיוק אותו דבר. ההבדל היחיד הוא שכשהחיבור נסגר, מסד הנתונים נעלם.

משורת הפקודה:

sqlite3 :memory:

עכשיו אתם בשורת הפקודה של SQLite עם מסד נתונים חדש וריק שקיים רק בזיכרון. צרו טבלה, הכניסו כמה שורות, שלפו אותן, הכול כרגיל:

צאו מהסשן והנתונים האלה מתאדים. לא נשאר שום קובץ, כי אף קובץ לא נוצר מלכתחילה.

למה שתרצו כזה

מסד נתונים שלא שורד הפעלה מחדש נשמע כמו באג, לא כמו תכונה. בפועל הוא שימושי בשלושה מצבים.

בדיקות. כל בדיקה מקבלת מסד נתונים נקי תוך אלפיות שנייה. בלי ניקוי קבצים זמניים, בלי מצב שנשאר מההרצה הקודמת, בלי קובץ fixture משותף שננעל. רוב חבילות הבדיקות ב-Python, Node ו-Go שמשתמשות ב-SQLite פותחות :memory: בדיוק מהסיבה הזו.

ניתוח חד-פעמי. טוענים CSV, מריצים כמה שאילתות, זורקים. מהיר יותר מהרמת מסד נתונים אמיתי וקל יותר מפענוח הקובץ בקוד בכל פעם.

מטמון ומרחב עבודה זמני. בתוך תוכנית שרצה זמן רב, מסד נתונים של SQLite בזיכרון הוא מנוע שאילתות אד-הוק טוב באופן מפתיע לנתונים שכבר טענתם.

המכנה המשותף: אתם רוצים SQL, ואתם לא רוצים שמירה קבועה.

ביצועים: מהיר יותר, אבל לא בקסם

מסדי נתונים בזיכרון מדלגים על הדיסק, כך שכתיבות שבדרך כלל היו מגיעות למערכת הקבצים הן רק עדכונים בזיכרון. עומסי עבודה שמוגבלים ב-I/O נעשים מהירים יותר באופן מורגש. עומסי עבודה שמוגבלים במעבד, כמו תכנון שאילתות מורכב או מיונים גדולים, כמעט לא משתנים, כי SQLite כבר שמרה במטמון את הדפים החמים בזיכרון בכל מקרה.

הדגמה מהירה של כמה התחביר זהה:

זה רץ מול מסד נתונים בזיכרון, אבל זה אותו SQL שהייתם מריצים מול קובץ. למנוע מסד הנתונים זה לא משנה.

בזיכרון מול קובץ: מתי לבחור מה

הפשרה פשוטה, וכדאי לנסח אותה במפורש:

  • מסד נתונים בקובץ (mydata.db): נשמר בין הפעלות. כמה תהליכים יכולים לפתוח אותו. שורד קריסות (עם מצב WAL, ברוב המקרים). השתמשו בו לכל דבר שצריך לזכור דברים.
  • מסד נתונים בזיכרון (:memory:): נעלם בסגירה. פרטי לחיבור שפתח אותו (כברירת מחדל). מהיר יותר לעבודה זמנית עם הרבה כתיבות. השתמשו בו לבדיקות, לעבודה זמנית ולמטמונים קצרי חיים.

אם אתם לא בטוחים, אתם רוצים קובץ. בזיכרון הוא המקרה המיוחד.

לכל חיבור יש אחד משלו

דבר עדין שתופס אנשים: פתיחה של :memory: פעמיים נותנת לכם שני מסדי נתונים נפרדים. הם לא חולקים טבלאות, לא חולקים נתונים ולא רואים זה את זה בכלל.

-- Terminal 1
sqlite3 :memory:
sqlite> CREATE TABLE t (x); INSERT INTO t VALUES (1);

-- Terminal 2
sqlite3 :memory:
sqlite> SELECT * FROM t;
Error: no such table: t

זה לא באג, זה העיצוב. :memory: פירושו "מסד נתונים פרטי לחיבור הזה". אותו דבר חל בתוך תוכנית אחת: אם הקוד שלכם פותח שני חיבורים ל-:memory:, כל אחד מקבל מסד נתונים מבודד משלו.

שיתוף מסד נתונים בזיכרון בין חיבורים

אם בכל זאת צריך שכמה חיבורים יראו את אותו מסד נתונים בזיכרון, 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 מקבל מסד נתונים אחר.

שמירה של מסד נתונים מהזיכרון לדיסק

לפעמים עושים תהליך עבודה שלם בזיכרון ואז מחליטים שרוצים לשמור את התוצאה. ל-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;

מקוד אפליקציה, ה-API של SQLite ב-C חושף את אותו מנגנון בשם sqlite3_backup_init, ורוב הספריות לשפות השונות עוטפות אותו. למודול sqlite3 של Python יש למשל את Connection.backup().

מלכודת נפוצה

אנשים מנסים לפעמים "לשמור" מסד נתונים מהזיכרון על ידי חיבור קובץ והעתקה:

זה עובד להעתקות פשוטות של טבלאות, אבל זה לא שומר על אינדקסים, טריגרים, views או מפתחות זרים בדיוק כפי שהיו. להעתק נאמן של מסד נתונים שלם, השתמשו ב-.backup (או ב-API של הגיבוי): הוא מבצע העתקה מדויקת ברמה הבינארית, דף אחר דף.

מה לקחת מכאן

  • :memory: הוא שם קובץ מיוחד של SQLite שיוצר מסד נתונים ב-RAM בלי קובץ מאחוריו.
  • ה-SQL זהה למסד נתונים בקובץ: אותן טבלאות, אותן שאילתות, אותם אילוצים.
  • כל חיבור ל-:memory: הוא פרטי; השתמשו ב-URI של shared cache (file::memory:?cache=shared) כשכמה חיבורים צריכים לחלוק אחד.
  • זה הכלי הנכון לבדיקות, לניתוח חד-פעמי ולמטמונים קצרי חיים, ולא לשום דבר שצריך לשרוד הפעלה מחדש.
  • העבירו מסד נתונים מהזיכרון לדיסק עם .backup כשאתם מחליטים שאתם רוצים לשמור אותו.

הצעד הבא: יצירת טבלאות

כבר ראיתם את CREATE TABLE חולף בכמה דוגמאות. העמוד הבא מאט ועובר עליו כמו שצריך: הגדרות עמודות, טיפוסים, האילוצים שאפשר להצמיד, והבחירות הקטנות שהופכות סכמה לנעימה לחיות איתה.

שאלות נפוצות

איך יוצרים מסד נתונים של SQLite בזיכרון?

פתחו את SQLite עם שם הקובץ המיוחד :memory: במקום נתיב. ב-CLI זה sqlite3 :memory:; בספרייה זו קריאת החיבור הרגילה, עם :memory: כשם הקובץ. מסד הנתונים חי ב-RAM ונעלם כשהחיבור נסגר.

מה זה :memory: ב-SQLite?

:memory: הוא שם קובץ קסום ש-SQLite מזהה כ'אל תשתמשי בקובץ, שמרי הכול ב-RAM'. מקבלים מסד נתונים מלא של SQLite, עם טבלאות, אינדקסים, טרנזקציות והכול, אבל שום דבר לא נכתב לדיסק לעולם. כל חיבור שפותח את :memory: מקבל מסד נתונים פרטי משלו.

האם שני חיבורים יכולים לשתף מסד נתונים של SQLite בזיכרון?

לא כברירת מחדל: כל חיבור ל-:memory: מבודד. כדי לשתף אחד, פתחו אותו עם URI כמו file::memory:?cache=shared והפעילו shared cache. כל חיבור שפותח בדיוק את ה-URI הזה באותו תהליך רואה את אותו מסד נתונים.

אפשר לשמור מסד נתונים של SQLite מהזיכרון לדיסק?

כן. השתמשו בפקודה .backup ב-CLI או ב-API של הגיבוי בספרייה שלכם כדי להעתיק את מסד הנתונים שבזיכרון לקובץ. אפשר גם לבצע ATTACH למסד נתונים בקובץ ולהריץ INSERT INTO file.table SELECT * FROM main.table כדי להעתיק את הנתונים.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל