בדף הזה מוסבר מה אפשר לעשות אם מסד הנתונים נתקל בהגנה מפני גלישת מזהה עסקה ב-PostgreSQL. ההודעה תופיע כהודעה ERROR, באופן הבא:
database is not accepting commands to avoid wraparound data loss in database dbname. Stop the postmaster and vacuum that database in single-user mode. You might also need to commit or roll back old prepared transactions, or drop stale replication slots.
לחלופין, יכול להיות שתופיע הודעה WARNING כזו:
database dbname must be vacuumed within 10985967 transactions. To avoid a database shutdown, execute a database-wide VACUUM in that database.
סקירה כללית של השלבים
- לבדוק איזה מסד נתונים ואיזה טבלאות גורמים להצגה חוזרת של נתונים.
- בודקים אם יש משהו שמונע את הפעולה (AUTO)VACUUM (לדוגמה, מזהה עסקה תקוע).
- מדידת המהירות של AUTOVACUUM. אם הוא איטי, אפשר לנסות לזרז אותו.
- אם צריך, מריצים עוד כמה פקודות VACUUM באופן ידני.
- כדאי לבדוק דרכים נוספות להאצת השאיבה. לפעמים הדרך הכי מהירה היא להסיר את הטבלה או חלק מהאינדקסים.
הרבה מההמלצות לערכים של דגלים לא מדויקות בכוונה, כי הן תלויות בהרבה פרמטרים של מסד הנתונים. בסוף הדף הזה יש קישורים למסמכים שבהם אפשר לקרוא ניתוח מעמיק יותר של הנושא.
איתור מסד הנתונים והטבלה שגורמים להחזרה לתחילת המחזור
איתור מסד הנתונים
כדי לגלות אילו מסדי נתונים מכילים את הטבלאות שגורמות לבעיה, מריצים את השאילתה הבאה:
SELECT datname,
age(datfrozenxid),
2^31-1000000-age(datfrozenxid) as remaining
FROM pg_database
ORDER BY 3
הבעיה היא במסד הנתונים עם הערך remaining שקרוב ל-0.
איך מוצאים את הטבלה
מתחברים למסד הנתונים ומריצים את השאילתה הבאה:
SELECT c.relnamespace::regnamespace as schema_name,
c.relname as table_name,
greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age,
2^31-1000000-greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as remaining
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm')
ORDER BY 4;
השאילתה הזו מחזירה את הטבלה או הטבלאות שגורמות לבעיה.
לטבלאות זמניות
אם schema_name מתחיל ב-pg_temp_, הדרך היחידה לפתור את הבעיה היא להסיר את הטבלה, כי PostgreSQL לא מאפשרת לבצע VACUUM בטבלאות זמניות שנוצרו בסשנים אחרים. לפעמים, אם הסשן הזה פתוח ונגיש, אפשר לבצע דחיסה של הטבלה, אבל זה לא קורה בדרך כלל.
כדי להסיר את הטבלה הזמנית, משתמשים בהצהרות ה-SQL הבאות:
SET cloudsql.enable_maintenance_mode = 'on'; /* get extra transaction ids */
DROP TABLE pg_temp_<N>.<tablename>;
אם זו הייתה החסימה היחידה, תוך דקה בערך, הפונקציה autovacuum תזהה את השינוי הזה ותקדם את datfrozenxid ב-pg_database. הפעולה הזו פותרת את הבעיה של מצב קריאה בלבד בהגנה על גלישת מספרים.
טבלאות רגילות
בטבלאות רגילות (כלומר לא זמניות), ממשיכים לשלבים הבאים כדי לבדוק אם יש משהו שמונע את הניקוי, אם הפקודה VACUUM פועלת מספיק מהר ואם הטבלה הכי חשובה עוברת ניקוי.
בדיקה אם מזהה העסקה תקוע
סיבה אפשרית אחת לכך שהמערכת עלולה להישאר ללא מזהי עסקאות היא ש-PostgreSQL לא יכול להקפיא (כלומר, לסמן כגלוי לכל העסקאות) מזהי עסקאות שנוצרו אחרי שהתחילה העסקה הכי ישנה שפועלת כרגע. הסיבה לכך היא כללי בקרת בו-זמניות לנתונים בכמה גרסאות (MVCC). במקרים קיצוניים, עסקאות כאלה יכולות להיות ישנות כל כך, עד שלא ניתן יהיה להשתמש בפקודה VACUUM כדי לנקות עסקאות ישנות במסגרת המגבלה של 2 מיליארד עסקאות, והמערכת תפסיק לקבל DML חדש. בדרך כלל מופיעות גם אזהרות בקובץ היומן, כמו WARNING: oldest
xmin is far in the past.
צריך לעבור לאופטימיזציה רק אחרי שפותרים את הבעיה עם מזהה העסקה התקוע.
בהמשך מפורטות ארבע סיבות אפשריות לכך שמזהה עסקה נתקע, עם מידע על דרכים לפתרון כל אחת מהן:
- עסקאות שפועלות במשך זמן רב: צריך לזהות אותן ולבטל או להפסיק את פעולת ה-בק-אנד כדי לבטל חסימה של הפעולה vacuum.
- עסקאות הכנה ללא הורה: מבטלים את העסקאות האלה.
- משבצות שכפול נטושות: צריך להסיר את המשבצות הנטושות.
- עסקה ארוכה ברפליקה, עם
hot_standby_feedback = on: צריך לזהות אותן ולבטל או להפסיק את פעולת ה-backend כדי לבטל את החסימה של ה-vacuum.
במקרים האלה, השאילתה הבאה מחזירה את הגיל של העסקה הכי ישנה ואת מספר העסקאות שנותרו עד להחלפה:
WITH q AS ( SELECT (SELECT max(age(backend_xmin)) FROM pg_stat_activity WHERE state != 'idle' ) AS oldest_running_xact_age, (SELECT max(age(transaction)) FROM pg_prepared_xacts) AS oldest_prepared_xact_age, (SELECT max(greatest(age(catalog_xmin),age(xmin))) FROM pg_replication_slots) AS oldest_replication_slot_age, (SELECT max(age(backend_xmin)) FROM pg_stat_replication) AS oldest_replica_xact_age ) SELECT *, 2^31 - oldest_running_xact_age AS oldest_running_xact_left, 2^31 - oldest_prepared_xact_age AS oldest_prepared_xact_left, 2^31 - oldest_replication_slot_age AS oldest_replication_slot_left, 2^31 - oldest_replica_xact_age AS oldest_replica_xact_left FROM q;
יכול להיות שהשאילתה הזו תחזיר כל אחד מהערכים *_left שדווחו כקרובים ל-1 מיליון או פחות מכך מההיפוך. הערך הזה הוא מגבלת ההגנה על גלישת מספרים כש-PostgreSQL מפסיק לקבל פקודות כתיבה חדשות. במקרה כזה, כדאי לעיין במאמר בנושא הסרת חסימות של VACUUM או במאמר בנושא התאמה של VACUUM.
לדוגמה, השאילתה הקודמת עשויה להחזיר:
┌─[ RECORD 1 ]─────────────────┬────────────┐ │ oldest_running_xact_age │ 2146483655 │ │ oldest_prepared_xact_age │ 2146483655 │ │ oldest_replication_slot_age │ ¤ │ │ oldest_replica_xact_age │ ¤ │ │ oldest_running_xact_left │ 999993 │ │ oldest_prepared_xact_left │ 999993 │ │ oldest_replication_slot_left │ ¤ │ │ oldest_replica_xact_left │ ¤ │ └──────────────────────────────┴────────────┘
כאשר oldest_running_xact_left ו-oldest_prepared_xact_left נמצאים במסגרת מגבלת ההגנה של מיליון דולר. במקרה כזה, כדי להמשיך, צריך קודם להסיר את החסימות של הפקודה VACUUM.
הסרת חסימות של VACUUM
עסקאות ממושכות
בשאילתה הקודמת, אם oldest_running_xact שווה ל-oldest_prepared_xact, צריך לעבור לקטע
Orphaned prepare transaction, כי הערך latest running כולל גם את העסקאות שהוכנו.
יכול להיות שקודם תצטרכו להריץ את הפקודה הבאה בתור המשתמש postgres:
GRANT pg_signal_backend TO postgres;
אם העסקה הבעייתית שייכת לאחד ממשתמשי המערכת (מתחילה ב-cloudsql...), אי אפשר לבטל אותה ישירות. כדי לבטל את העדכון, צריך להפעיל מחדש את מסד הנתונים.
כדי לזהות שאילתה שפועלת במשך זמן רב ולבטל או להפסיק אותה כדי לבטל את החסימה של פעולת ה-vacuum, צריך קודם לבחור כמה מהשאילתות הכי ישנות. הקו LIMIT 10 עוזר להתאים את התוצאה למסך. יכול להיות שתצטרכו לחזור על הפעולה הזו אחרי שתפתרו את הבעיות בשאילתות הכי ישנות שפועלות.
SELECT pid, age(backend_xid) AS age_in_xids, now() - xact_start AS xact_age, now() - query_start AS query_age, state, query FROM pg_stat_activity WHERE state != 'idle' ORDER BY 2 DESC LIMIT 10;
אם הערך שמוחזר מ-age_in_xids הוא NULL, המשמעות היא שלא הוקצה לעסקה מזהה עסקה קבוע ואפשר להתעלם ממנה בבטחה.
מבטלים את השאילתות שבהן הערך של xids_left_to_wraparound מתקרב למיליון.
אם הערך של state הוא active, אפשר לבטל את השאילתה באמצעות SELECT pg_cancel_backend(pid);. אחרת, צריך לסיים את כל החיבור באמצעות SELECT pg_terminate_backend(pid);, כאשר pid הוא pid מהשאילתה הקודמת
עסקאות הכנה ללא הורה
מציגים רשימה של כל העסקאות שהוכנו:
DB_NAME=> SELECT age(transaction),* FROM pg_prepared_xacts ; ┌─[ RECORD 1 ]┬───────────────────────────────┐ │ age │ 2146483656 │ │ transaction │ 2455493932 │ │ gid │ trx_id_pin │ │ prepared │ 2021-03-03 16:54:07.923158+00 │ │ owner │ postgres │ │ database │ DB_NAME │ └─────────────┴───────────────────────────────┘
מבטלים את השינויים בעסקאות המוכנות הכי ישנות שאין להן הורה באמצעות gid מהשאילתה האחרונה (במקרה הזה, trx_id_pin) כמזהה העסקה:
ROLLBACK PREPARED trx_id_pin;
אפשר גם לבצע commit:
COMMIT PREPARED trx_id_pin;
הסבר מלא זמין במאמר בנושא SQL ROLLBACK PREPARED.
משבצות רפליקציה שננטשו
אם משבצת השכפול ננטשת כי העותק הקיים הופסק, הושהה או שיש בו בעיה אחרת, אפשר למחוק את העותק ממסוף gcloud או Google Cloud .
קודם כל, צריך לוודא שהרפליקה לא מושבתת, כמו שמתואר במאמר בנושא ניהול רפליקות לקריאה. אם הרפליקה מושבתת, הפעל אותה שוב. אם השהייה עדיין גבוהה, מוחקים את הרפליקה,
אפשר לראות את חריצי השכפול בתצוגת המערכת pg_replication_slots
system view.
השאילתה הבאה מאחזרת את המידע הרלוונטי:
SELECT *, age(xmin) AS age FROM pg_replication_slots; ┌─[ RECORD 1 ]────────┬─────────────────────────────────────────────────┐ │ slot_name │ cloudsql_1_355b114b_9ff4_4bc3_ac88_6a80199bd738 │ │ plugin │ ¤ │ │ slot_type │ physical │ │ datoid │ ¤ │ │ database │ ¤ │ │ active │ t │ │ active_pid │ 1126 │ │ xmin │ 2453745071 │ │ catalog_xmin │ ¤ │ │ restart_lsn │ C0/BEF7C2D0 │ │ confirmed_flush_lsn │ ¤ │ │ age │ 59 │ └─────────────────────┴─────────────────────────────────────────────────┘
בדוגמה הזו, הערך pg_replication_slots תקין (age == 59).
אם הגיל היה קרוב ל-2 מיליארד, כדאי למחוק את משבצת המיקום. אם השאילתה מחזירה כמה רשומות, אין דרך קלה לדעת איזו רפליקה היא איזו. לכן, כדאי לבדוק את כולם למקרה שיש טרנזקציה ממושכת באחת מהרפליקות.
עסקאות ממושכות בעותקים משוכפלים
בודקים את העותקים המשוכפלים כדי למצוא את העסקה הכי ישנה שפועלת עם hot_standby_feedback
מוגדר ל-on ומשביתים אותה בעותק המשוכפל.
בעמודה backend_xmin בתצוגה pg_stat_replication מוצג TXID הכי ישן שנדרש ברפליקה.
כדי להעביר אותו קדימה, צריך להפסיק את השאילתה שמעכבת אותו בעותק. כדי לגלות איזו שאילתה מעכבת את התהליך, משתמשים בשאילתה בLong running transactions, אבל הפעם מריצים אותה על הרפליקה.
אפשרות נוספת היא להפעיל מחדש את הרפליקה.
הגדרת VACUUM
מגדירים את שני הדגלים הבאים:
- autovacuum_vacuum_cost_delay = 0
- autovacuum_work_mem = 1048576
הפקודה הראשונה משביתה את ההגבלה של קצב העברת הנתונים בדיסק לצורך דחיסה על ידי PostgreSQL, כדי שהפקודה VACUUM תוכל לפעול במהירות מלאה. כברירת מחדל, המערכת מגבילה את השימוש ב-autovacuum כדי שלא ינצל את כל קלט/פלט הדיסק בשרתים הכי איטיים.
הדגל השני, autovacuum_work_mem, מקטין את מספר המעברים של ניקוי האינדקס. אם אפשר, הוא צריך להיות גדול מספיק כדי לאחסן את כל המזהים של השורות המתות בטבלה שהפקודה VACUUM תנקה. כשמגדירים את הערך הזה, צריך לזכור שזהו הסכום המקסימלי של זיכרון מקומי שכל הפעלה של VACUUM יכולה להקצות. חשוב לוודא שלא מאפשרים יותר ממה שזמין, ומשאירים חלק מהרישיונות כרזרבה. אם משאירים את מסד הנתונים במצב קריאה בלבד, צריך לקחת בחשבון גם את הזיכרון המקומי שמשמש לשאילתות לקריאה בלבד.
ברוב המערכות, משתמשים בערך המקסימלי (1GB או 1048576KB, כמו שמוצג בדוגמה). הערך הזה מתאים לעד כ-178 מיליון טופלים לא פעילים. כל תוספת של עוד אינדקסים תגרום למעברים מרובים בסריקת האינדקס.
הסברים מפורטים על הדגלים האלה ועל דגלים אחרים זמינים במאמר אופטימיזציה, מעקב ופתרון בעיות של פעולות VACUUM ב-PostgreSQL.
אחרי שמגדירים את הדגלים האלה, מפעילים מחדש את מסד הנתונים כדי שהפונקציה autovacuum תתחיל לפעול עם הערכים החדשים.
אפשר להשתמש בתצוגה pg_stat_progress_vacuum כדי לעקוב אחרי ההתקדמות של פקודות VACUUM שהופעלו על ידי autovacuum. בתצוגה הזו מוצגים תהליכי VACUUM שפועלים בכל מסדי הנתונים, וגם טבלאות (יחסים) ממסדי נתונים אחרים שלא ניתן לחפש את שם הטבלה שלהם באמצעות עמודת התצוגה relid.
כדי לזהות את מסדי הנתונים והטבלאות שצריך לבצע בהם פעולת VACUUM בהמשך, משתמשים בשאילתות מתוך אופטימיזציה, מעקב ופתרון בעיות של פעולות VACUUM ב-PostgreSQL. אם מכונת ה-VM של השרת חזקה מספיק ויש לה רוחב פס לתהליכי VACUUM מקבילים נוספים מעבר לאלה שהופעלו על ידי autovacuum, אפשר להפעיל כמה תהליכי VACUUM ידניים.
בדיקת המהירות של VACUUM
בקטע הזה מוסבר איך לבדוק את המהירות של הפקודה VACUUM ואיך להאיץ אותה, אם צריך.
בדיקה של פעולות אוטומטיות של ניקוי זיכרון מטמון
כל ה-backends שמריצים VACUUM מוצגים בתצוגת המערכת pg_stat_progress_vacuum.
אם השלב הנוכחי הוא scanning heap, אפשר לעקוב אחרי ההתקדמות באמצעות שינויים בעמודה heap_blks_scanned.
לצערנו, אין דרך קלה לקבוע את מהירות הסריקה בשלבים אחרים.
הערכת מהירות הסריקה של VACUUM
כדי להעריך את מהירות הסריקה, צריך קודם לאחסן את ערכי הבסיס ואז לחשב את השינוי לאורך זמן כדי להעריך את זמן הסיום. קודם צריך לשמור תמונה של heap_blks_scanned עם חותמת זמן באמצעות שאילתת התמונה הבאה:
SELECT set_config('save.ts', clock_timestamp()::text, false), set_config('save.heap_blks_scanned', heap_blks_scanned::text, false) FROM pg_stat_progress_vacuum WHERE datname = 'DB_NAME';
מכיוון שאי אפשר לשמור נתונים בטבלאות שכבר נמצאות במצב של גלישה חוזרת, צריך להשתמש ב-set_config(flag, value) כדי להגדיר שני דגלים שמוגדרים על ידי המשתמש – save.ts ו-save.heap_blks_scanned – לערכים הנוכחיים מ-pg_stat_progress_vacuum.
בשאילתה הבאה, אנחנו משתמשים בשני אלה כבסיס להשוואה כדי לקבוע את המהירות ולחשב את הזמן המשוער לסיום.
הערה: WHERE datname = DB_NAME מגביל את החקירה למסד נתונים אחד בכל פעם. המספר הזה מספיק אם יש רק תהליך אחד של ניקוי אוטומטי שפועל במסד הנתונים הזה, עם יותר משורה אחת לכל מסד נתונים. צריך להוסיף תנאי סינון נוספים ל-WHERE כדי לציין שורה אחת של autovacuum.('AND relid= …'') זה נכון גם לגבי השאילתה הבאה.
אחרי ששומרים את ערכי הבסיס, אפשר להריץ את השאילתה הבאה:
with q as ( SELECT datname, phase, heap_blks_total, heap_blks_scanned, clock_timestamp() - current_setting('save.ts')::timestamp AS ts_delta, heap_blks_scanned - current_setting('save.heap_blks_scanned')::bigint AS scanned_delta FROM pg_stat_progress_vacuum WHERE datname = DB_NAME ), q2 AS ( SELECT *, scanned_delta / extract('epoch' FROM ts_delta) AS pages_per_second FROM q ) SELECT *, (heap_blks_total - heap_blks_scanned) / pages_per_second AS remaining_time FROM q2 ;
┌─[ RECORD 1 ]──────┬──────────────────┐ │ datname │ DB_NAME │ │ phase │ scanning heap │ │ heap_blks_total │ 9497174 │ │ heap_blks_scanned │ 18016 │ │ ts_delta │ 00:00:40.30126 │ │ as_scanned_delta │ 11642 │ │ pages_per_second │ 288.87434288655 │ │ remaining_time │ 32814.1222418038 │ └───────────────────┴──────────────────┘
השאילתה הזו משווה את הערכים הנוכחיים לערכי הבסיס השמורים ומחשבת את pages_per_second ואת remaining_time, כדי שנוכל להחליט אם הפקודה VACUUM פועלת מספיק מהר או אם אנחנו רוצים להאיץ אותה. הערך של remaining_time מתייחס רק לשלב scanning heap.
גם שלבים אחרים לוקחים זמן, לפעמים אפילו יותר. אפשר לקרוא מידע נוסף על שאיבת אבק ולעיין בפוסטים בבלוג באינטרנט שדנים בכמה מההיבטים המורכבים של שאיבת אבק.
האצת הפקודה VACUUM
הדרך הקלה והמהירה ביותר להאיץ את הסריקה של VACUUM היא להגדיר את autovacuum_vacuum_cost_delay=0. אפשר לעשות זאת דרך מסוףGoogle Cloud .
לצערנו, הפקודה VACUUM שכבר פועלת לא מזהה את הערך הזה, ויכול להיות שתצטרכו להפעיל מחדש את מסד הנתונים.
אחרי הפעלה מחדש, יכול להיות שתראו תוצאה דומה לזו:
┌─[ RECORD 1 ]──────┬──────────────────┐ │ datname │ DB_NAME │ │ phase │ scanning heap │ │ heap_blks_total │ 9497174 │ │ heap_blks_scanned │ 222382 │ │ ts_delta │ 00:00:21.422615 │ │ as_scanned_delta │ 138235 │ │ pages_per_second │ 6452.76031894332 │ │ remaining_time │ 1437.33713040171 │ └───────────────────┴──────────────────┘
בדוגמה הזו, המהירות עלתה מ-300 דפים לשנייה לפחות ל-6,500 דפים לשנייה בערך, וזמן הסיום המשוער של שלב סריקת הערימה ירד מ-9 שעות ל-23 דקות.
קשה יותר למדוד את מהירות הסריקה של השלבים האחרים, אבל היא אמורה להיות דומה.
כדאי גם להגדיל את autovacuum_work_mem ככל האפשר כדי להימנע ממעברים מרובים על אינדקסים. העברת אינדקס מתרחשת בכל פעם שהזיכרון מתמלא במצביעים של טאפלים לא פעילים.
אם לא נעשה שימוש במסד הנתונים למטרות אחרות, צריך להגדיר את autovacuum_work_mem כך שיהיה זמין בזיכרון נפח של כ-80% אחרי שמאפשרים את הכמות הנדרשת ל-shared_buffers.
זו המגבלה העליונה לכל אחד מתהליכי ה-VACUUM שהופעלו על ידי autovacuum. אם רוצים להמשיך להריץ עומסי עבודה לקריאה בלבד, צריך להשתמש בפחות זיכרון.
דרכים נוספות לשיפור המהירות
הימנעות מניקוי אינדקסים
בטבלאות גדולות מאוד, הפקודה VACUUM מבלה את רוב הזמן בניקוי האינדקסים.
ב-PostgreSQL 14 יש אופטימיזציות מיוחדות למניעת ניקוי אינדקסים אם המערכת נמצאת בסכנה של גלישת מספרים.
ב-PostgreSQL 12 ו-13, אפשר להריץ באופן ידני את ההצהרה הבאה:
VACUUM (INDEX_CLEANUP OFF, TRUNCATE OFF) <tablename>;
בגרסאות 11 ומטה, אפשר DROP את האינדקס לפני שמריצים את הפקודה vacuum וליצור אותו מחדש מאוחר יותר.
אם מפעילים autovacuum על טבלה מסוימת, כדי להסיר את האינדקס צריך לבטל את ה-vacuum שפועל ואז להריץ מיד את הפקודה drop index לפני שה-autovacuum יצליח להפעיל שוב vacuum על הטבלה.
קודם כול, מריצים את ההצהרה הבאה כדי למצוא את ה-PID של תהליך ה-autovacuum שצריך להפסיק:
SELECT pid, query
FROM pg_stat_activity
WHERE state != 'idle'
AND query ilike '%vacuum%';
לאחר מכן מריצים את ההצהרות הבאות כדי להפסיק את פעולת ה-vacuum ולהסיר אינדקס אחד או יותר:
SET cloudsql.enable_maintenance_mode = 'on'; /* get extra transaction ids */
SELECT pg_terminate_backend(<pid>);DROP INDEX <index1>;DROP INDEX <index2>; ...
הסרת הטבלה שמפרה את המדיניות
במקרים נדירים, אפשר להסיר את הטבלה. לדוגמה, אם מדובר בטבלה שקל לשחזר ממקור אחר כמו גיבוי או מסד נתונים אחר.
עדיין צריך להשתמש ב-cloudsql.enable_maintenance_mode = 'on', וכנראה גם להפסיק את הפעולה VACUUM בטבלה הזו, כמו שמוצג בקטע הקודם.
VACUUM FULL
במקרים נדירים, עדיף להריץ את הפקודה VACUUM FULL FREEZE, בדרך כלל כשהטבלה מכילה רק חלק קטן של טופלים פעילים.
אפשר לבדוק את זה בתצוגה pg_stat_user_tables (אלא אם הייתה קריסה שמחקה את הנתונים הסטטיסטיים).
הפקודה VACUUM FULL מעתיקה טופלים פעילים לקובץ חדש, ולכן צריך להיות מספיק מקום פנוי לקובץ החדש ולאינדקסים שלו.
המאמרים הבאים
- מידע נוסף על VACUUM for wraparound
- מידע נוסף על שאיבת אבק שגרתית
- מידע נוסף על שאיבת אבק אוטומטית
- מידע נוסף על אופטימיזציה, מעקב ופתרון בעיות בפעולות VACUUM ב-PostgreSQL