הגדרת מנוע ClickHouse MergeTree לאנליטיקה

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

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

מדוע אנליטיקה על ClickHouse MergeTree מדרדרת עם גידול הנתונים

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

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

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

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

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

מה נותנת סכימת אחסון נכונה: אפקט בזמנים ובנפחים

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

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

חלוקה למחיצות לפי חודש יחד עם מדיניות TTL הופכת את האחסון לניהולי: מחיצות מיושנות נמחקות בשלמותן, בלי לכתוב מחדש את הטבלה ובלי לפגוע בהכנסות. דחיסה עמודתית יחד עם התאמת קודק לאופי העמודות — בנפרד מספריות, בנפרד מילוניות דרך LowCardinality — נותנת דחיסה פי 4–8 ביחס לנפח המקורי. המאגר מפסיק לגדול באופן ליניארי יחד עם זרם האירועים.

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

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

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

אפשרויות לארגון אחסון אנליטי: השוואת גישות

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

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

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

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

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

שלבי הגדרת אחסון אנליטי

הגדרת MergeTree מתחילה לא בבחירת המנוע, אלא בהבנה אילו שאילתות לאחסון מבוצעות מדי יום. סדר העבודה כמעט זהה בכל הטמעה: קודם מודדים, אחר כך מתכננים, ורק потом בודקים בעומס הקרוב למציאות. להלן הרצף שלפיו הולכים המהנדסים שלנו.

  1. ביקורת שאילתות ופרופיל עומס. אוספים רשימה של דוחות קבועים ושליפות חד-פעמיות, בודקים מדידות זמן לכל אחת, ומסמנים אילו שדות מסוננים ומקובצים בתדירות הגבוהה ביותר. התוצאה — רשימת שאילתות שבאמת כדאי למטב, ואלה שאפשר לא לגעת בהן.
  2. תכנון הסכימה. קובעים את הרכב הטבלאות, סוגי העמודות והחלוקה לשכבות: נתוני גלם, אגרגטים ביניים ומחושבים מראש. זה מסיר חישובים כבדים חוזרים בכל פנייה ללוח הדוחות.
  3. בחירת מפתח המיון. סדר העמודות במפתח קובע אם השאילתה תיכנס לטווח קריאה צר או תסרוק את כל הטבלה. אפשרויות בודקים על שליפות אמיתיות מהביקורת, ולא לפי אינטואיציה.
  4. הגדרת האחסון. מחלקים את הנתונים לפי תאריכים, קובעים תוחלת חיים וכללים נפרדים לתקופות שנשאלות לעיתים רחוקות. נפח האחסון מפסיק לגדול ללא בקרה, ובמקביל ההיסטוריה באופק הנדרש נשארת זמינה, וחתכים ישנים נקראים בלי דרדור.
  5. חישוב מראש של דוחות חוזרים. מוציאים את האגרגציות התכופות ביותר לתצוגות ממומשות, כדי שהלוחות ייפתחו תוך שניות ולא יחשבו מחדש מיליוני שורות בכל פנייה.
  6. בדיקה בעומס אמיתי. מריצים תרחישים טיפוסיים בו-זמנית, כפי שיהיה בעבודה: בודקים זמן תגובה, צריכת זיכרון ודיסק, והתנהגות בכתיבה בשיא. כאן גם בודקים שהחזרה לסכימה הקודמת עוברת בלי אובדן נתונים.
  7. מסירה לתפעול. מוסרים נהלי תחזוקה, הגדרות ניטור ותהליכים מתועדים של גיבוי ושחזור, כדי שהצוות יוכל לתחזק את האחסון בעצמו.

ערכת ארטיפקטים: סכימות טבלאות, קונפיגורציות, נהלים

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

  • סכימות טבלאות — סקריפטי DDL עם המנוע MergeTree: מפתח מיון, חלוקה למחיצות, כללי TTL ותצוגות ממומשות. לכל טבלה הערה מדוע נבחר דווקא המפתח הזה ומה יישבר אם מחליפים אותו.
  • קונפיגורציות אחסון — פרמטרים של מיזוג חלקים, מגבלות זיכרון לשאילתה, הגדרות רפליקציה וטבלאות מבוזרות, קודקי דחיסה מופעלים לפי עמודות.
  • מדיניות גישה — תפקידים ורמות הרשאה: חשבונות נפרדים לשירותי כתיבה, לאנליטיקאים ולמהנדס תורן; מכסות לשליפות כבדות; לחלק מהצוותים — גישה לקריאה בלבד.
  • נוהל תחזוקה — לוח זמנים למיזוג מחיצות, בקרה על גידול מספר החלקים, סדר פעולות במקרה של פיגור רפליקה וכללי חזרה לסכימה הקודמת.
  • תרשים זרימת נתונים — מהיכן ובאיזו תדירות מגיעים הנתונים, היכן הם נאגרים, ומה לעשות במקרה של עיכוב במקור או כפילויות.
  • ניטור והתראות — רשימת מדדים, ערכי סף ורשימת מי שמקבל התראה, עם עדיפויות.
  • הוראות תפעול — פעולות טיפוסיות, ניתוח תקלות נפוצות וגבולות שמעבר להם צריך לקרוא למהנדס.
  • יומן החלטות — אילו אפשרויות של סכימה ואחסון נשקלו, אילו נדחו ומדוע: זה חוסך זמן בעדכון הבא.

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

מקרה בוחן: איך האיצו שאילתות אנליטיות על נתוני זרם

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

נקודת ההתחלה

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

מה שינו

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

מה קרה בסופו של דבר

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

במה MergeTree שונה ממנועים אחרים של ClickHouse?

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

משפחת Log (Log, TinyLog, StripeLog) בנויה פשוט יותר: עמודה — קובץ נפרד, אין אינדקס ואין מיזוגים. טבלאות כאלה זולות בכתיבה ומתאימות לטבלאות עזר ולחוצצים ביניים, אבל שליפה לפי מפתח קוראת את הקובץ בשלמותו, והכתיבה המקבילית מוגבלת.

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

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

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

ספקות טכניים לפני בנייה מחדש של האחסון

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

האם הנתונים יאבדו בזמן בנייה מחדש של טבלאות?

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

האם נצטרך לכתוב מחדש דוחות וייצואים?

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

מה יקרה לפרודקשן הפעיל?

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

נדון במשימה ונחזור עם הערכה ותוכנית

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

  • מהיכן מגיעים הנתונים: תור, אספן לוגים, ייצוא שינויים ממסד הפעלה — באיזו תדירות ובאיזה פורמט מגיעים האירועים.
  • פרופיל עומס: כמה שורות ביממה ובחודש, זרימה ממוצעת וזרימת שיא, עומק האחסון הנדרש.
  • שאילתות טיפוסיות: 5-10 השכיחות ביותר עם מסננים, קיבוצים ותקופה — הן בדיוק אלה שקובעות את סדר המיון במפתח ואת החלוקה למחיצות.
  • טריות הנתונים: השיהוי המותר בין האירוע להופעתו בדוח — שניות או שעות; מזה תלויה תדירות ההכנסות וקבוצת האגרגטים המובנים.
  • עמידות לתקלות: האם נדרשות רפליקות, כמה עותקי נתונים, והאם יש צמתים נפרדים לתיאום הקלאסטר.
  • מצב נוכחי: היכן ייפרס, כמה מקום יש בדיסקים, מה כבר ניסו ואילו צווארי בקבוק ראו — מיזוגים כבדים, מספר חלקים מנופח, joins איטיים בין טבלאות.

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