Skip to content

דחיסה עמודתית ב־TimescaleDB: קידודים, ממשקים לפי גרסה ומדידה

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

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

TimescaleDB מרחיבה את PostgreSQL באמצעות hypertables המחולקות לפי זמן ותכנון אחסון משולב של שורות ועמודות. זו אפשרות לבדיקה, לא פתרון אוניברסלי.

TimescaleDB משלבת ייצוג עמודתי עם קידודים מותאמי טיפוס תוך שמירת גישה ב־SQL. אין מדובר רק בהרצת gzip על קובצי המסד. שיעור הצמצום תלוי בערכים, בסדר, בקיבוץ ובתקורה; אין כאן הבטחה או מדידה לצמצום של 90%.

האתגר המרכזי בדחיסת סדרות זמן

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

טבלאות heap של PostgreSQL שומרות רשומות בדפים בגודל קבוע, בדרך כלל 8 KiB. התרשים הבא מראה רק רוחבי ערכים נבחרים, לא רשומה פיזית מלאה: כותרות, יישור, מידע על null ואינדקסים מוסיפים תקורה. מקרא: timestamp — חותמת זמן, 8 בתים; device_id — מזהה מכשיר, 4 בתים; temperature — טמפרטורה, 8 בתים; status_code — קוד מצב, 2 בתים.

| timestamp (8 bytes) | device_id (4 bytes) | temperature (8 bytes) | status_code (2 bytes) |

קיבוץ ערכי אותה עמודה יכול לחשוף למקודד ערכים חוזרים ושינויים קטנים. אין בכך שיעור דחיסה אוניברסלי, ואין פירוש הדבר ש־PostgreSQL מפעילה LZ4 או ZSTD על שורות שלמות. דחיסת TOAST של PostgreSQL עוסקת בערכים מתאימים באורך משתנה, לא בתמונת השורה הפשוטה הזאת בת ארבע העמודות.

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

שלב 1: הפיכת שורות לעמודות

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

מערך סינתטי בן שלוש שורות מדגים את הרעיון; אלה אינם נתוני ניטור שרת שנצפו. מקרא: Timestamp — חותמת זמן; Device_ID — מזהה מכשיר; Temp — טמפרטורה.

Timestamp  | Device_ID | Temp
-----------+-----------+-----
10:00:00   | A101      | 21.5
10:00:01   | A101      | 21.6
10:00:02   | A101      | 21.5

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

  • חותמות זמן: [10:00:00, 10:00:01, 10:00:02]
  • מזהי מכשירים: [A101, A101, A101]
  • טמפרטורות: [21.5, 21.6, 21.5]

ערכים דומים וסדר מתאים יכולים לסייע לקידודים מותאמי טיפוס. הדחיסה עדיין כוללת מטא־נתונים ועלולה להיות לא יעילה באצוות קטנות או בעלות אנטרופיה גבוהה.

שלב 2: אלגוריתמי דחיסה מותאמי טיפוס

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

1. דחיסת הפרש־הפרשים, Delta-of-Delta, לחותמות זמן

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

המסלול המתועד למספרים שלמים ולחותמות זמן משלב שינוי להפרש־הפרשים, קידוד ערכים בעלי סימן ואריזת Simple-8b/RLE. השלבים הבאים מסבירים את דפוס ההפרשים, לא את הפורמט הבינרי המלא:

  • שומרים את חותמת הזמן הראשונה כנקודת ייחוס.
  • עבור חותמות הזמן הבאות מחשבים את ההפרש, delta, מחותמת הזמן הקודמת.
  • אם הנתונים נדגמים באופן סדיר, ההפרשים יהיו כמעט זהים, למשל תמיד 1,000 מילישניות.
  • מחשבים את ״הפרש ההפרשים״ — ההפרש בין ההפרש הנוכחי לקודם. אם המרווח סדיר לחלוטין, הערך הוא אפס.
  • Simple-8b אורז כמה מספרים שלמים קטנים במילים בגודל קבוע; הרחבת RLE שלו מייצגת ערכים חוזרים באופן קומפקטי. החיסכון תלוי ברצף שעבר המרה ובתקורת נקודות הייחוס והמטא־נתונים, לא במספר קבוע של סיביות לכל חותמת זמן.

2. דחיסה מבוססת XOR למספרי נקודה צפה

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

TimescaleDB משתמשת בדחיסה מבוססת XOR, שגם היא הותאמה מ־Gorilla. היא משווה את ערך הנקודה הצפה הנוכחי לקודם באמצעות פעולת XOR על הסיביות.

המקודד בסגנון Gorilla מנצל אפסים ב־XOR של ייצוגי נקודה צפה סמוכים ושומר מספיק מידע בקרה לשחזור כל ערך. דפוסים חוזרים יכולים לחסוך מקום; ערכים רועשים ומטא־נתונים מצמצמים את הרווח. זהו קידוד ללא אובדן, לא ויתור על דיוק מספרי.

3. קידוד אורך רצף ודחיסת מילון לנתונים בעלי מספר קטן של ערכים שונים

עמודות כמו device_id,‏ status_code או location עשויות לחזור, אך יש לבחון את מספר הערכים השונים ואת אורכי הרצפים במקום להסיק אותם מהשמות.

בהמחשה כללית של RLE, רצף עוקב של 10,000 ערכי status_code השווים ל־'OK' ניתן לתיאור באמצעות ('OK', 10000) במקום לחזור על 'OK'. זה אינו פורמט אחסון המחרוזות המילולי של TimescaleDB: קידוד המילון שלה שומר ערכים נבדלים ודוחס את רצף האינדקסים שלהם באמצעות Simple-8b/RLE.

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

4. חלופת מערך ו־TOAST של PostgreSQL

החלופה המתועדת שומרת ערכים בייצוג דמוי מערך ללא דחיסה משלה; TOAST של PostgreSQL יכול לדחוס את הערך או לאחסן אותו מחוץ לשורה. שיטות TOAST הזמינות כוללות pglz וגם LZ4 כאשר נבנה עם תמיכה. אין להסיק מהכינוי ״אחסון עמודתי״ שקיימת חלופה אוניברסלית של LZW או ZSTD.

המחשת תצורה: ממשקי Hypercore לפי גרסה

TimescaleDB 2.18 הציגה שמות API חדשים לאחסון עמודתי במקום שמות הדחיסה הישנים. ה־SQL להלן מוגבל במפורש לדור הממשק של 2.18 ולמסד דוגמה ריק שניתן להשליך; אין זו המלצה לפרוס מהדורה ישנה. אמתו גרסה מותקנת, תאימות PostgreSQL, מהדורה ורישיון, הרשאות, גיבויים ותיעוד עדכני לפני שינוי מורשה כלשהו. לא הורצו פקודות מסד נתונים.

יצירת Hypertable

הסכימה שלא הורצה יוצרת טבלת דוגמה חדשה וריקה ומשתמשת ב־API הכללי by_range ל־hypertable. היא אינה מסבה טבלת ייצור קיימת או קולטת נתוני ניטור שרת אמיתיים.

-- Unexecuted illustration for the TimescaleDB 2.18 API generation.
-- Assumes a disposable database with a compatible extension already available.
CREATE TABLE sys_metrics_demo (
    time TIMESTAMPTZ NOT NULL,
    device_id INT NOT NULL,
    cpu_utilization DOUBLE PRECISION,
    status TEXT
);
SELECT create_hypertable('sys_metrics_demo', by_range('time'));
-- No real telemetry is inserted; no existing table is migrated.

הפעלת הגדרות האחסון העמודתי

בדור API זה, segmentby מציין ערכי קיבוץ ו־orderby מסדר ערכים בתוך הקבוצות. העמודות הנבחרות צריכות להתאים לדפוס השאילתות ולהותיר קבוצות גדולות מספיק.

-- Unexecuted, continuing the empty demo schema above.
ALTER TABLE sys_metrics_demo SET (
    timescaledb.enable_columnstore = true,
    timescaledb.segmentby = 'device_id',
    timescaledb.orderby = 'time DESC'
);

הפשרה בקיבוץ: השם הישן compress_segmentby מקביל ל־timescaledb.segmentby. קיבוץ ערכי device_id = 1 יכול לסייע לשאילתות המסננות לפי מכשיר; ערך הקיבוץ נשמר בנפרד ללא דחיסת רצף עמודה. יותר מדי קבוצות שונות או מעט מדי שורות בקבוצה יכולים להפחית דחיסה. מזהה מכשיר אינו מבטיח מתאם.

תכנון מדיניות אחסון עמודתי

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

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

-- Unexecuted. Only after confirming no policy exists for this demo table.
-- add_columnstore_policy is a PROCEDURE, so use CALL, not SELECT.
CALL add_columnstore_policy(
    'sys_metrics_demo', after => INTERVAL '7 days'
);

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

מדידת תוצאות אחסון

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

-- Read-only query illustration, not executed here.
SELECT total_chunks, number_compressed_chunks,
       before_compression_total_bytes,
       after_compression_total_bytes
FROM hypertable_columnstore_stats('sys_metrics_demo');
-- No output is claimed for the empty example table.

לצורך חשבון בלבד, נניח שאותו מערך נתונים תפס 1.2 TB עשרוניים לפני המרה ו־98 GB עשרוניים אחריה. אלה מספרים מומצאים, לא תצפית ייצור או פלט מה־SQL לעיל:

  • לפני, בהנחה: 1,200 GB
  • אחרי, בהנחה: 98 GB
  • צמצום היפותטי: (1 − 98 / 1,200) × 100 ≈ 91.83%; יחס הגדלים הוא בערך 12.24:1. אף מספר אינו חוזה מערך נתונים אחר או את החשבון הסופי.

ההשפעה על ביצועי שאילתות

חשש נפוץ הוא: ״האם פריסת הדחיסה לא תאט את השאילתות?״

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

  1. פחות I/O יכול לעזור: דחיסה עשויה לצמצם בתים הנקראים, אך הדיסק אינו תמיד צוואר הבקבוק. צמצום אחסון אינו מעיד על צמצום זהה ב־I/O של שאילתה או בזמן שלה.
  2. סינון מקטעים: תנאי זמן מתאימים יכולים לפסול מקטעים שטווחיהם אינם יכולים להתאים. השאלה אם מדלגים על מקטעים עמודתיים היסטוריים תלויה בטווחים, בתנאים ובתוכנית בפועל, לא רק בביטוי ״24 השעות האחרונות״.
  3. סינון לפי קבוצות: סינון לפי device_id יכול לדלג על אצוות לא קשורות כשהוא מפתח קיבוץ. ההגדרה הישנה compress_segmentby אינה מבטיחה שייפרס וקטור אחד בדיוק; העמודות, האצוות ואסטרטגיית הביצוע שנבחרו עדיין חשובות.

סיכום: מדדו את הפשרה בין אחסון לשאילתות

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

הבינו את הקידוד, בחרו קיבוץ וסדר לפי העומס, השתמשו ב־APIs של הגרסה המותקנת ומדדו גם אחסון וגם שאילתות. הפרש־הפרשים, XOR וטכניקות מילון ו־RLE הם מנגנונים, לא הבטחה לחשבונות קטנים או ל־SQL מהיר.

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

מקורות ראשוניים

Post a Comment

Previous Post Next Post