Table Partitioning ב-Postgres: חלוקת טבלאות ענק לפי טווח או Hash

מאת צוות מדיה דיל · 03.09.2026 · טכנולוגיה · 6 דק׳

Range, Hash ו-List Partitioning, איך Partition Pruning חוסך סריקות מיותרות, ניהול אוטומטי עם pg_partman, ומתי Partitioning מיותר בכלל.

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

מה בדיוק נשבר בטבלה ענקית לא מחולקת

VACUUM ו-ANALYZE רצים על הטבלה כולה גם כשרק חלק קטן ממנה השתנה, אינדקס B-Tree על טבלה בגודל כזה יכול לתפוס יותר זיכרון מהזמין ל-Cache, ומחיקת נתונים ישנים (DELETE) על מיליוני שורות נועלת ומייצרת Bloat אדיר. Partitioning הופך את כל אלה לבעיות ברמת פלח בודד, לא ברמת הטבלה כולה — VACUUM על פלח אחד קטן לוקח שניות במקום שעות על הטבלה השלמה.

Range Partitioning: החלוקה הנפוצה ביותר

חלוקה לפי טווח — לרוב לפי תאריך — מתאימה במיוחד לטבלאות לוג, אירועים ומדדים שגדלים באופן כרונולוגי. כל חודש או יום מקבל Partition נפרד, מה שהופך מחיקת נתונים ישנים לפעולת DROP TABLE מיידית על הפלח הרלוונטי במקום DELETE איטי שסורק שורה-שורה.

Hash Partitioning: כשאין ציר טבעי

כשאין עמודה שמתחלקת בטבעיות לטווחים (כמו תאריך), Hash Partitioning מפזר שורות בין Partitions לפי פונקציית Hash על עמודת מפתח — מבטיח פיזור אחיד יחסית בלי תלות בערכים עצמם, ומונע מצב שבו Partition אחד גדול משמעותית מהאחרים. החיסרון: קשה לחזות מראש לאיזה Partition שורה ספציפית תגיע, ואי אפשר להוסיף או להסיר Partition בקלות בלי לערבב מחדש חלק מהנתונים, מה שמקשה על תחזוקה ידנית ממוקדת.

List Partitioning: חלוקה לפי ערכים דיסקרטיים

כשיש קבוצה סגורה וברורה של ערכים — למשל region או tenant_id — List Partitioning מקצה Partition לכל ערך או קבוצת ערכים. זה שימושי כשרוצים גם בידוד תפעולי בין קבוצות לקוחות וגם ביצועים, בדומה לרעיון המרכזי בDatabase Sharding, רק בתוך אותו מסד נתונים במקום פיזור בין שרתים נפרדים.

Partition Pruning: הסיבה האמיתית לביצועים טובים יותר

כשהשאילתה כוללת תנאי WHERE על עמודת החלוקה, מתכנן השאילתות של Postgres מדלג לגמרי על Partitions שלא רלוונטיים — לא סורק אותם, לא פותח אותם. זה ה-Partition Pruning, וזה המנגנון שהופך שאילתה שהייתה סורקת מיליארד שורות לשאילתה שסורקת רק את הפלח הרלוונטי, בלי לשנות שורת קוד באפליקציה.

אינדקסים על Partitioned Table: לא אוטומטי

אינדקס שמוגדר על הטבלה ההורה (Partitioned Table) לא נוצר אוטומטית כאינדקס פיזי מאוחד — הוא מוגדר בנפרד על כל Partition. חשוב לוודא שהאינדקסים על עמודות הסינון הנפוצות קיימים על כל פלח בפועל, ולא רק בהגדרה הלוגית — עקרונות בחירת אינדקס נכון נשארים זהים לאלה שבאינדוקס ב-Postgres, רק מוכפלים לכל Partition.

ניהול Partitions בפרודקשן

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

מתי Partitioning הוא מורכבות מיותרת

מתחת לכמה מיליוני שורות, המחיר התפעולי של Partitioning — מורכבות Migrations, ניהול Constraints ייחודיים שחייבים לכלול את עמודת החלוקה, וכאב ראש בשאילתות Cross-Partition — כמעט תמיד עולה על התועלת. הכלל המעשי: לא מתחילים עם Partitioning, מוסיפים אותו כשיש עדות מדידה אמיתית שגודל הטבלה הוא צוואר הבקבוק.

טבלה שגדלה מהר מדי והשאילתות מתחילות להאט? נשמח לבדוק איתכם אסטרטגיית חלוקה מתאימה בוואטסאפ.

תגיות: Postgres · Table Partitioning · Database Performance · Partition Pruning

← חזרה לבלוג · צור קשר