שתי משימות תחזוקה שונות
ANALYZE ו-VACUUM מוזכרים באותה נשימה, אבל הם מתקנים בעיות שונות.
ANALYZEאוסף סטטיסטיקות על הנתונים שלכם כדי שמתכנן השאילתות יקבל החלטות חכמות יותר. הוא כותב לטבלה בשםsqlite_stat1ולא נוגע בשורות עצמן.VACUUMבונה מחדש את הקובץ עצמו כדי לשחרר דפים שאינם בשימוש ולבטל פרגמנטציה באחסון. הוא לא משנה ישירות תוכניות שאילתה.
אם שאילתות בוחרות באינדקס הלא נכון, אתם צריכים ANALYZE. אם הקובץ גדול ממה שהוא אמור להיות אחרי הרבה מחיקות, אתם צריכים VACUUM. בלבול ביניהם מוביל להרבה זמן תחזוקה מבוזבז.
מה ANALYZE עושה בפועל
מתכנן השאילתות נאלץ לנחש. כשהוא רואה WHERE status = 'active', הוא צריך להעריך כמה שורות מתאימות, אחת? מיליון?, כדי להחליט אם להשתמש באינדקס או לסרוק את הטבלה. בלי סטטיסטיקות, הוא נופל חזרה להיוריסטיקות גסות.
ANALYZE עובר על כל אינדקס ורושם מידע מסכם על התפלגות הערכים:
השורה ב-sqlite_stat1 אומרת למתכנן בערך כמה שורות יש באינדקס וכמה כפילויות יש למפתח טיפוסי. בפעם הבאה שתשאלו WHERE status = 'pending', הוא יודע ש-pending נדיר ופונה לאינדקס; עבור WHERE status = 'shipped', הוא עשוי להחליט שסריקה זולה יותר.
אפשר להריץ ניתוח על טבלה אחת או על אינדקס אחד במקום על כל מסד הנתונים:
ANALYZE orders;
ANALYZE idx_orders_status;
הריצו ANALYZE אחרי טעינות גדולות, אחרי שינויי סכמה גדולים, או כשאתם שמים לב שהמתכנן בוחר תוכניות גרועות בטבלאות שההתפלגות שלהן השתנתה.
PRAGMA optimize: ברירת המחדל המודרנית
הרצה עיוורת של ANALYZE בכל סגירת חיבור היא בזבוז: רוב הזמן, שום דבר לא השתנה מספיק כדי שזה ישנה משהו. SQLite מגיעה עם עטיפה חכמה יותר:
PRAGMA optimize בודקת איך מסד הנתונים השתנה מאז הניתוח האחרון ומריצה ANALYZE רק על טבלאות שצריכות את זה. ההמלצה הרשמית היא לקרוא לה בכל חיבור ארוך טווח ממש לפני הסגירה, ובאופן תקופתי בחיבורים שנשארים פתוחים שעות.
היא זולה כששום דבר לא השתנה ויעילה כשמשהו כן. פנו קודם ל-optimize; פנו ל-ANALYZE הגולמי רק כשצריך לכפות רענון.
מה VACUUM עושה בפועל
כשמוחקים שורות או מוחקים טבלה, SQLite מסמנת את הדפים האלה כפנויים אבל לא מקטינה את הקובץ. הדפים הפנויים משמשים שוב בהכנסות עתידיות, כך שרוב הזמן זה בסדר. אבל שני דברים מצטברים לאורך חיים של הרבה שינויים:
- מקום פנוי שמערכת ההפעלה לא רואה. קובץ ה-
.dbשלכם נשאר בגודל 2 GB למרות שרק 800 MB הם נתונים חיים. - פרגמנטציה. שורות של אותה טבלה מתפזרות על פני דפים לא סמוכים, וזה פוגע בביצועי הסריקה.
VACUUM מתקן את שניהם על ידי העתקת כל מסד הנתונים לקובץ חדש, ארוז בצפיפות, והחלפת המקורי:
אחרי VACUUM, הקובץ בגודל שהיה לו אילו הכנסתם מאפס רק את 100 השורות ששרדו. כתופעת לוואי, כל ה-rowids נשארים זהים אבל הפריסה בדיסק שוב רציפה.
כמה דברים שכדאי לדעת לפני שמריצים אותו:
- הוא צריך נעילה בלעדית על מסד הנתונים לכל משך הפעולה. אף חיבור אחר לא יכול לכתוב.
- הוא צריך מקום פנוי בדיסק של בערך פי שניים מגודל מסד הנתונים: הוא בונה את הקובץ החדש לצד הישן.
- אי אפשר להריץ אותו בתוך טרנזקציה, והוא יחזיר שגיאה אם יש טרנזקציות פעילות פתוחות.
- על מסד נתונים של כמה GB, זה יכול לקחת הרבה זמן. תכננו את זה מראש.
מתי באמת להריץ VACUUM
לרוב האפליקציות: אל תריצו, אלא אם משהו ספציפי השתנה.
סיבות טובות להריץ VACUUM:
- הרגע מחקתם טבלה גדולה או מקבץ ענק של שורות ואתם רוצים את המקום בדיסק בחזרה.
- מסד הנתונים עובר שינויים כבר שנים, ושאילתות שסורקות טבלאות מרגישות איטיות יותר מבעבר.
- אתם מפיצים קובץ מסד נתונים כחלק מגרסה ורוצים שיהיה קטן ככל האפשר.
סיבות גרועות:
- "ליתר ביטחון." הפקודה כותבת מחדש את כל הקובץ בכל פעם. אין שום דבר בטוח בלעשות את זה על מערכת חיה.
- אחרי כל מקבץ מחיקות. הדפים שהשתחררו היו משמשים שוב בכל מקרה.
auto_vacuum ו-VACUUM הדרגתי
אם אתם רוצים ש-SQLite תנהל דפים פנויים אוטומטית, מגדירים את auto_vacuum בזמן יצירת מסד הנתונים: אי אפשר לשנות את זה אחר כך בלי vacuum מלא:
PRAGMA auto_vacuum = INCREMENTAL;
שלושה מצבים:
NONE(ברירת המחדל): דפים פנויים נשארים בקובץ ומשמשים שוב בהכנסות מאוחרות יותר.FULL: כל commit שמשחרר דפים גם מקצץ את הקובץ. נוח, אבל כל טרנזקציה משלמת את המחיר.INCREMENTAL: SQLite עוקבת אחרי דפים פנויים אבל משחררת אותם רק כשמבקשים:
PRAGMA incremental_vacuum(N) משחררת עד N דפים פנויים בחזרה למערכת ההפעלה: מהיר, לא מחזיק נעילה בלעדית לאורך זמן, ואפשר להריץ את זה לפי לוח זמנים. זו נקודת האיזון המושלמת למסדי נתונים עם הרבה כתיבות שצריכים להישאר קומפקטיים בלי המחיר של VACUUM מלא.
VACUUM INTO: ייצוא עותק קומפקטי
VACUUM INTO כותבת עותק חדש וקומפקטי לקובץ חדש בלי לגעת במקורי:
VACUUM INTO 'backup.db';
זה שימושי באמת:
- גיבויים. הפלט הוא תמונת מצב עקבית ודחוסה לגמרי: בלי דפים שנכתבו חלקית, בלי
.walשצריך לדאוג לו. עדיף על העתקת הקובץ עםcp. - הקטנה בלי לחסום כותבים לאורך זמן. מריצים vacuum לקובץ צדדי, ואז מחליפים באופן אטומי. הכותבים לא חסומים לכל משך ה-vacuum.
- הפצה. שלחו עותק זעיר ונטול פרגמנטציה של מסד נתונים לפיתוח.
קובץ היעד לא יכול להיות קיים. אם הוא קיים, תקבלו שגיאה.
מתכון תחזוקה מעשי
למסד נתונים טיפוסי של אפליקציה:
-- בכל חיבור ארוך טווח, לפני הסגירה:
PRAGMA optimize;
-- אחרי טעינה גדולה או שינוי סכמה:
ANALYZE;
-- אחרי שמחקתם הרבה נתונים ורוצים את המקום בדיסק בחזרה:
VACUUM;
-- לגיבויים:
VACUUM INTO '/backups/app-2026-04-23.db';
אם מסד הנתונים מונע בעיקר מכתיבות ומחיקות ונשאר מחובר 24/7, הגדירו auto_vacuum = INCREMENTAL בזמן היצירה והריצו PRAGMA incremental_vacuum(N) מדי פעם, למשל פעם ביום בשעות של תנועה נמוכה.
איך מאבחנים "למה הקובץ שלי כל כך גדול?"
שתי פקודות pragma מראות מה קורה:
page_count×page_size= גודל הקובץ הנוכחי.freelist_count×page_size= בייטים שמבוזבזים על דפים שאינם בשימוש.
אם freelist_count הוא חלק גדול מ-page_count, VACUUM (או incremental_vacuum) יקטין את הקובץ באופן מורגש. אם הוא קטן, הקובץ כבר ארוז ביעילות ו-VACUUM לא יעזור.
מלכודות נפוצות
- הרצת
VACUUMבתוך טרנזקציה. זה לא אפשרי. בצעו commit קודם. - לשכוח ש-
VACUUMצריך מקום פנוי בדיסק. מסד נתונים של 10 GB צריך עוד כ-10 GB פנויים כדי לבצע vacuum. - הגדרת
auto_vacuumאחרי שכבר יש נתונים. אין לזה שום השפעה עד ה-VACUUMהמלא הבא. הגדירו את זה כשיוצרים את מסד הנתונים אם אתם רוצים את זה. - הרצת
ANALYZEוציפייה לקבצים קטנים יותר. זה התפקיד שלVACUUM. - הרצת
VACUUMוציפייה לתוכניות שאילתה טובות יותר. זה התפקיד שלANALYZE.
שתי הפקודות משלימות זו את זו; אף אחת לא מחליפה את השנייה.
הבא בתור: טרנזקציות
פקודות תחזוקה כמו VACUUM מבליטות משהו שלקחנו כמובן מאליו: מודל הטרנזקציות של SQLite ומה הוא נועל, ומתי. הפרק הבא מתחיל שם: איך טרנזקציות עובדות, מה BEGIN / COMMIT / ROLLBACK באמת מבטיחים, ואיך להשתמש בהם כדי לשמור על עבודה של כמה פקודות כפעולה אטומית.
שאלות נפוצות
מה ההבדל בין ANALYZE ל-VACUUM ב-SQLite?
ANALYZE אוסף סטטיסטיקות על התוכן של טבלאות ואינדקסים ושומר אותן בטבלה sqlite_stat1, שממנה מתכנן השאילתות קורא אותן כדי לבחור תוכניות טובות יותר. VACUUM בונה מחדש את קובץ מסד הנתונים מאפס כדי לשחרר דפים שאינם בשימוש ולבטל פרגמנטציה באחסון. הם פותרים בעיות שונות: ANALYZE הופך את השאילתות לחכמות יותר, VACUUM הופך את הקובץ לקטן יותר.
באיזו תדירות כדאי להריץ VACUUM ב-SQLite?
רוב מסדי הנתונים אף פעם לא צריכים את זה. הריצו VACUUM אחרי DELETE או DROP TABLE גדולים אם גודל הקובץ חשוב לכם, או מדי פעם על מסדי נתונים ותיקים עם הרבה כתיבות שעברו הרבה שורות. הפקודה כותבת מחדש את כל הקובץ ולוקחת נעילה בלעדית, כך שזה לא משהו לתזמן בקלות ראש. לניקוי הדרגתי אוטומטי, הגדירו PRAGMA auto_vacuum = INCREMENTAL כשיוצרים את מסד הנתונים.
מה עושה PRAGMA optimize?
PRAGMA optimize היא ההמלצה המודרנית: הריצו אותה לפני סגירת חיבורים ו-SQLite תחליט אם ANALYZE (או תחזוקה אחרת) באמת שווה את זה, לפי האופן שבו מסד הנתונים השתנה. היא זולה יותר מהרצה עיוורת של ANALYZE, וזה מה שרוב האפליקציות צריכות לקרוא לו בזמן כיבוי.