אופטימיזציית ClickHouse: חלוקה (Partitioning), אינדקסים ותצוגות מהירות (Materialized Views)

שאילתות איטיות ועומס דיסק גבוה בעבודה עם ClickHouse הם בעיה מוכרת שמאטה את צמיחת העסק. אנחנו צוות מהנדסים המתמחה באופטימיזציה של ClickHouse: אנו מגדירים partitioning, מיישמים skip indexes ו-materialized views כדי להאיץ את ביצוע השאילתות. אנו מספקים פרויקטים turnkey—מביקורת סכימה ועד תמיכה שוטפת—ומבטיחים פעילות יציבה של האנליטיקה שלכם.

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

שאלות נפוצות

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

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

אופטימיזציית ClickHouse: חלוקה למחיצות, אינדקסים ותצוגות מהותיות

שאילתת SELECT על טבלה עם 2 טריליון שורות לוקחת 40 שניות. קלט/פלט בדיסק ב-100%, השרת מזמזם. נשמע מוכר? בדרך כלל, זה אומר שמפתח ראשי נבחר בצורה גרועה, החלוקה למחיצות לא תואמת את דפוס השאילתות, או שהנתונים נשאלים ללא ניצול אינדקסי דילוג. אנחנו צוות מהנדסים עם ניסיון של 10+ שנים בהפעלת ClickHouse בסביבת ייצור. הגישה שלנו מספקת תוצאות מדידות: חיסכון טיפוסי בזמן ביצוע שאילתות של פי 5–10, והפחתת עומס דיסק עד 70%. צרו קשר לבדיקת סכמה—נעריך את פוטנציאל האופטימיזציה תוך יום אחד.

למה חלוקה למחיצות היא לא פתרון קסם

טעות נפוצה: יצירת טבלה עם PARTITION BY toYYYYMM(event_date) וציפייה לניסים. חלוקה למחיצות ב-ClickHouse היא בעיקר כלי לניהול נתונים (TTL, מחיקת מחיצות ישנות), לא דרך להאיץ שאילתות. שאילתה שמסננת לפי חודש ספציפי אכן מדלגת על מחיצות לא רלוונטיות, אבל אם מסנן התאריך רופף או חסר—ClickHouse קורא הכל.

סיפור אמיתי: לקוח חילק טבלת אירועים לפי toYYYYMMDD(ts)—365 מחיצות בשנה. זה גרם ליצירת אלפי חלקים זעירים בכל הכנסה לילית, MergeTree לא הצליח לעמוד במיזוגים, ושאילתות החלו לפתוח מאות מתארי קבצים. יומן המערכת היה מלא ב-Too many parts (300). Merges are processing significantly slower than inserts. חזרה לחלוקה חודשית וכיוונון merge_with_ttl_timeout פתרו את הבעיה תוך שעה.

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

איך לבחור את ORDER BY הנכון?

ב-ClickHouse, ORDER BY מגדיר את סדר המיון הפיזי של הנתונים על הדיסק ומשמש כמפתח ראשי (אם PRIMARY KEY לא צוין במפורש). גודל הגרנולה ברירת המחדל הוא 8192 שורות. כל גרנולה מתוארת באינדקס דליל, ו-ClickHouse מחליט אילו גרנולות לקרוא על סמך אינדקס זה.

אם האלמנט הראשון של ORDER BY הוא user_id UUID, אבל כל השאילתות מסננות לפי event_type ו-ts, האינדקס חסר תועלת—ClickHouse קורא את כל הגרנולות.

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

CREATE TABLE events (
    event_type LowCardinality(String),
    project_id UInt32,
    ts DateTime,
    user_id UUID,
    payload String
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(ts)
ORDER BY (event_type, project_id, ts)
SETTINGS index_granularity = 8192;

שאילתה כמו CREATE TABLE events ( event_type LowCardinality(String), project_id UInt32, ts DateTime, user_id UUID, payload String ) ENGINE = MergeTree() PARTITION BY toYYYYMM(ts) ORDER BY (event_type, project_id, ts) SETTINGS index_granularity = 8192; קוראת כעת רק את טווח הגרנולות הדרוש.

איך פועלים אינדקסי דילוג?

WHERE event_type = 'click' AND project_id = 42 AND ts >= now() - INTERVAL 1 YEAR, minmax, set, bloom_filter הם ארבעת הסוגים העיקריים של אינדקסי דילוג. הם לא מחליפים את המפתח הראשי אלא משלימים אותו עבור עמודות שאינן ב-tokenbf_v1.

ORDER BY חיוני לחיפושי UUID או hash:

ALTER TABLE events ADD INDEX idx_user_id user_id TYPE bloom_filter(0.01) GRANULARITY 4; 

לאחר מימוש (bloom_filter), שאילתה כמו ALTER TABLE events ADD INDEX idx_user_id user_id TYPE bloom_filter(0.01) GRANULARITY 4; מדלגת על גרנולות שבהחלט חסר בהן אותו UUID. עם שיעור חיובי שגוי של 1% ו-1000 גרנולות, רק 10-50 נקראות בפועל במקום כולן.

WHERE user_id = '...' עובד על טוקנים של מחרוזות—שימושי לחיפושי תת-מחרוזת ביומנים ללא tokenbf_v1.

סוג אינדקס מתי להשתמש קרדינליות משוערת
minmax טווחים (תאריך, טמפרטורה) נמוכה–בינונית
set ערכים מדויקים (סטטוס, קטגוריה) עד עשרות אלפים
bloom_filter חיפושי UUID, hash כל שהיא
tokenbf_v1 חיפוש תת-מחרוזת בטקסט כל שהיא

מהן תצוגות מהותיות ומתי להשתמש בהן?

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

CREATE MATERIALIZED VIEW events_hourly_mv TO events_hourly AS
SELECT
    toStartOfHour(ts) AS hour,
    event_type,
    project_id,
    count() AS cnt,
    uniqHLL12(user_id) AS uniq_users
FROM events
GROUP BY
    hour,
    event_type,
    project_id;

טבלת היעד משתמשת ב-LIKE '%pattern%' עם CREATE MATERIALIZED VIEW events_hourly_mv TO events_hourly AS SELECT toStartOfHour(ts) AS hour, event_type, project_id, count() AS cnt, uniqHLL12(user_id) AS uniq_users FROM events GROUP BY hour, event_type, project_id; :

CREATE TABLE events_hourly (
    hour DateTime,
    event_type LowCardinality(String),
    project_id UInt32,
    cnt AggregateFunction(count),
    uniq_users AggregateFunction(uniqHLL12, UUID)
) ENGINE = AggregatingMergeTree()
ORDER BY (hour, event_type, project_id);

שאילתות על נתונים מצטברים רצות פי 10-100 מהר יותר מאשר על נתונים גולמיים. אבל שימו לב: תצוגות מהותיות מתעדכנות רק בהכנסות חדשות. יש להוסיף נתונים היסטוריים ידנית דרך AggregatingMergeTree.

איך לנתח שאילתות?

לפני ביצוע שינויים, בדקו את AggregateFunction:

SELECT query_id, query, read_rows, read_bytes, result_rows, elapsed, memory_usage
FROM system.query_log
WHERE type = 'QueryFinish'
  AND query_start_time >= now() - INTERVAL 1 HOUR
ORDER BY elapsed DESC
LIMIT 20;

CREATE TABLE events_hourly ( hour DateTime, event_type LowCardinality(String), project_id UInt32, cnt AggregateFunction(count), uniq_users AggregateFunction(uniqHLL12, UUID) ) ENGINE = AggregatingMergeTree() ORDER BY (hour, event_type, project_id); נותן את יחס הקריאה היתרה. אם אתם קוראים 100 GB עבור תוצאה של 1 MB—האינדקס לא עובד או חסר.

INSERT INTO ... SELECT מראה את מצב המחיצות ומספר החלקים. אם system.query_log עבור מחיצה אחת—MergeTree לא יכול לעמוד במיזוגים; בדקו את SELECT query_id, query, read_rows, read_bytes, result_rows, elapsed, memory_usage FROM system.query_log WHERE type = 'QueryFinish' AND query_start_time >= now() - INTERVAL 1 HOUR ORDER BY elapsed DESC LIMIT 20; ו-read_bytes.

איך אנחנו מייעלים את ClickHouse

אנחנו מתחילים בבדיקה: אוספים את 20 השאילתות הכבדות המובילות מ-result_bytes, בודקים סכמות טבלאות, ומנתחים באמצעות system.parts. זה חושף 80% מהבעיות.

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

יישום: יצירת טבלאות חדשות, הגדרת תצוגות מהותיות, העברת נתונים בקבוצות דרך count(*) > 300. בדיקות ביצועים מקבילות משוות background_pool_size לפני ואחרי על שאילתות זהות.

לאחר ההעברה, אנו מספקים תיעוד: איזה אינדקס לאילו שאילתות, איך לנטר max_bytes_to_merge_at_max_space_in_pool, מתי להריץ system.query_log.

מקרה בוחן: חנות מסחר אלקטרוני עם 5 מיליארד אירועים ביום

שאילתות על אירועים גולמיים בזמן אמת פיגרו אחרי לוחות המחוונים. הוספנו אינדקס דילוג bloom_filter על user_id, הגדרנו חלוקה חודשית למחיצות, ויצרנו תצוגה מהותית לצבירות שעתיות. זמן ביצוע שאילתה טיפוסי ירד מ-30 שניות ל-0.3 שניות, ועומס האשכול ירד פי 3.

לוחות זמנים והיקף משוערים

היקף המשימה נפח נתונים לוח זמנים
אופטימיזציה של 1-2 טבלאות עד 1 TB 1-2 ימים
בדיקת סכמה מלאה + תצוגות מהותיות 1-10 TB 2-3 ימים
העברה עם יצירת טבלה מחדש 10+ TB 3-5 ימים

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

מה כלול

  • ניתוח סכמה ופרופיל 20 השאילתות הכבדות המובילות (EXPLAIN, system.query_log)
  • עיצוב חלוקה למחיצות, מפתח ראשי ואינדקסי דילוג
  • יצירה והגדרה של תצוגות מהותיות
  • העברת נתונים עם זמן השבתה מינימלי (העברה מתגלגלת)
  • תיעוד תפעולי: המלצות ניטור, שאילתות טיפוסיות, מדריך פעולות
  • הדרכת צוות: איך לעבוד עם הסכמה החדשה, איך להוסיף שדות חדשים

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