למה אני אוהב את ClickHouse?

·

אחד השמות העולים בעולם הדטאבייסים, בדגש על דטאבייסים המיועדים לאחסון כמויות גדולות של מידע, הוא ClickHouse.
ClickHouse אינו שחקן חדש בשוק. למעשה, מדובר בדטאבייס שהפיתוח שלו התחיל עוד בשנת 2012, כפרוייקט פנימי של חברת Yandex שמטרתו הייתה לתמוך את המוצר המתחרה שלהם ל-Google Analytics שנקרא Metrica.

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

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

  • טבלאות Event-ים: אם ה- workload שלכם מכיל חלק משמעותי של טבלאות שמייצגות event-ים, שהם immutable (לא משתנים או נמחקים), מאד יכול להיות שתרוויחו משמירה שלהם ב-ClickHouse. לא ממש משנה האם האירועים הללו מייצגים לוגים, מידע מעולמות הניטור, מידע מעולמות האבטחה, או דברים שספציפיים לעולם התוכן שבו אתם עובדים.
  • שימוש OLAP-י :שליפות מאסיביות (שיכולות להנות מיכולות ה- Massive Parallel Processing של CH) שמנתחות הרבה מידע שרובו נמצא בטבלה אחת
  • מידע שהוא time-series:טבלאות שבהן המידע מסודר לפי זמן, ושליפות רבות מתעניינות במידע בטווחי זמן ספציפיים, יוכלו להנות מהיכולת של ClickHouse לגשת למידע שלפיו הטבלה מסודרת בצורה יעילה מאד.

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

הוא מהיר.

אם תחפשו על ClickHouse בגוגל, תגלו שזאת הסיבה המרכזית בגלל הרבה מהמשתמשים מתחילים לעבוד איתו.
אחת הטבלאות הראשונות שטענתי כאשר שיחקתי עם ClickHouse כללה מאות מיליוני שורות, שמכילות בין היתר עמודת טקסט קצרה (כ-200-300 תווים), שעליה עשיתי שליפת LIKE. זה מהיר. מאד מהיר. למעשה, בהתחשב בעובדה שהמידע על שורת הטקסט לא היה מאונדקס קודם (בדומה לאלסטיק או מנועים דומים), הפה שלי נפער כשראיתי קצב סריקה של מאות מיליוני שורות בשנייה.
כשמדובר בפילטור וביצוע אגרגציות על טבלאות (בייחוד כאלה שלא מחזירות הרבה קבוצות), לא נדיר לראות ש- ClickHouse מטפל במיליארדי שורות בשנייה, ללא אופטימיזציות מיוחדות. כמובן, שברגע שמכניסים לתמונה אופטימיזציות, גם כמויות גדלות משמעותיות של מידע קטנות עליו (קיימים בעולם cluster-ים של פטות).

אתם כמובן לא צריכים רק להאמין למה שיש לי להגיד בנושא. באתר ה- benchmarks שלהם, משווים את עצמם ב-ClickHouse מול מנועים שונים שמריצים חומרות שונות.
אם תחפשו בגוגל את השילוב בין ClickHouse למנוע ה-DB האהוב עליכם, בטח תמצאו שפע של פוסטים על השוואות שהניעו אנשים לעבור (בגלל הביצועים) ממוצרים אחרים ל-CH. מוטיב מרכזי שחוזר על עצמו, הוא כמות החומרה הקטנה יותר ש-CH צריך ביחס למתחרים כדי לתת את אותם הביצועים, מה שכמובן הופך אותו להרבה יותר cost-effective.

הוא יעיל בשמירת המידע.

כאשר מדברים על data-warehouse-ים, או סתם שמירת טבלאות ענק, אחד הפרמטרים החשובים הוא כמה טוב המידע נדחס וכמה יעילה/מהירה הדחיסה שבה נעשה שימוש כדי לשמור את המידע (כי כאשר אנחנו רוצים לשלוף, אנחנו משלמים את זמן העיבוד של ה- decompress).

ClickHouse מפורסם ביכולות שמירת המידע היעילות שלו, שדוחסות את המידע ביחסים גבוהים (פי 5 זה נפוץ, אבל גם פי 10 ויותר אפשרי, כמובן כתלות במידע).

שמירת המידע היעילה מתאפשרת בזכות מספר גורמים:

  • שמירת מידע columnar-ית: פורמט הטבלאות העיקרי שבו ClickHouse עושה שימוש, MergeTree, שומר את המידע בתצורה של עמודות. לכל עמודה יש קובץ (יש קצת דקויות הנוגעות למקרה שיש מעט מאד שורות בטבלה, אבל אנחנו מדברים על המקרה הנפוץ). בתצורת השמירה הזאת מתאפשרת דחיסה טובה משמעותית של הערכים ביחס לשמירה של המידע ב- row-format (תחת ההנחה שעמודה תכיל בסבירות גבוהה יותר חזרתיות של ערכים בתוך עצמה, מאשר בין עמודות שונות)
  • דחיסה: ClickHouse דוחס את המידע בטבלאות בדיפולט באמצעות LZ4, שהוא אלגוריתם דחיסה שמשלב בין דחיסה טובה לבין דחיסה קלה-חישובית (שמאפשרת אח"כ סריקה מהירה יותר של המידע). ניתן להשתמש גם באלגוריתמים אחרים של דחיסה, למשל כאשר מעדיפים דחיסה אגרסיבית יותר על חשבון צריכת CPU גבוהה יותר בשליפת המידע
  • צמצום המידע שנשמר באמצעות הגדרות על העמודה: מעבר לדחיסה של העמודות, CH מאפשר הגדרות נוספות כדי להקטין את הנפח שטבלה לוקחת בפועל. למשל, אם יש עמודה שיש בה חזרתיות מאד גבוהה של מס' מצומצם של ערכים (שמות רחובות וערים, user-agent-ים של גלישות וכו'), ניתן להגדיר את העמודה בתור LowCardinality(String), במקום String רגיל. בצורה הזאת, CH שומר dictionary שממפה בין מספר ל-string המלא, אך בערכים עצמם שומר רק את המספר (וכך חוסך את נפח ה- string, ללא קשר לדחיסה).
    דוגמא נוספת היא השימוש ב- CODEC-ים כדוגמת Delta שמאפשר, עבור ערכים שמשתנים לאט, לשמור רק את ערך השינוי ולא את הערך המלא. כך ניתן לחסוך, ובמקום לשמור 8 בתים לכל ערך של תאריך ושעה מדוייקים, לשמור רק את ה- diff (שיכול להיכנס בפחות). כמובן שזאת רק דוגמא, וקיימים עוד CODEC-ים מגוונים ומתוחכמים יותר.

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

  • מידע של לוגים אצלנו נשמר גם ב- ClickHouse וגם באלסטיק (לתקופת זמן מוגבלת יותר). המידע ב-CH שוקל רק 28% מנפח המידע ב- Elasticsearch (וזה לא מנורמל: ב- CH יש 3 העתקים של המידע ובאלסטיק שניים). זה מאפשר לנו לשמור לוגים ב-CH למשך זמן ארוך משמעותית מזה שאנחנו שומרים באלסטיק (למעשה, המוטיבציה העיקרית לאלסטיק בהקשר הזה היא בכלל ה-GUI של Kibana והנוחות למשתמשים)
  • בטבלה שמכילה מידע שהוא יותר מובנה (ללא עמודות טקסט חופשי) וחזרתי (שמאפשר להשתמש ב- LowCardinality ב-CH), הפרשי הנפחים נעשים משמעותיים אף יותר: אותה תקופת זמן לוקחת באלסטיק פי 14 יותר נפח. גם אם מניחים שלא נשמור את ה- _source באלסטיק ונחסוך עוד קצת נפח, עדיין מדובר בפערים משמעותיים מאד.

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

הוא מבוסס SQL, אבל על סטרואידים.

כאשר עובדים מול CH משתמשים בוריאנט ה- SQL של ClickHouse.
עצם השימוש ב- SQL הוא כבר יתרון משמעותי מבחינתי (בהשוואה לשימוש ב-DSL ייעודי כלשהו למוצר). במקרה של ClickHouse, ואריאנט ה- SQL שהם עשו הוא פשוט מוצלח במיוחד, מה שמגדיל עוד יותר את היתרון.
יש עושר רב של פונקציות שימושיות שנותנות מענה לפחות-או-יותר כל דבר שרק אפשר לחשוב עליו, יש שינויים מסויימים בהשוואה ל-SQL שהופכות את כתיבת השאילתות לנוחה יותר ויש מנוע שיודע לתמוך בכמעט כל סוג שליפה שתכתבו.

בין הדברים שאהבתי במיוחד בוריאנט SQL של CH:

  • היכולת להגדיר alias לעמודה או לביטוי ב- SELECT ולהשתמש בו לאחר מכן ב- WHERE וב- GROUP BY מפשטת את הכתיבה בהשוואה ל-SQL
  • ה-combinators השונים שאפשר לצרף לפונקציות, הופכות את כתיבת השאילתות לקריאה יותר. למשל, זאת דוגמא לשליפת SQL מול CH, שמי שכתב SQL מול DB אחר יוכל להעריך את הפשטות והאלגנטיות שלה:
SELECT 
	toStartOfFifteenMinutes(DateCreated) as t,
	countIf(MetricValue = 500) as NumErrors,
	countIf(MetricValue = 200) as NumSuccess,
	argMax(Username, ElapsedMs) as UserThatWaitedLongestInTimeFrame
FROM tbl
GROUP BY t
HAVING NumErrors > 0
ORDER BY t

קל להקים ולתחזק אותו עצמאית ו- on-prem

הרבה מהמוצרים בעולמות ה- data שיצאו בשנים האחרונות שמו את עיקר הדגש בעולם ה- cloud, הרבה פעמים באופן בלעדי – ללא תמיכה כלל בהרצה on-prem. חלקם עשו את זה כהחלטה עסקית, וחלקם הם פשוט מוצרים של ספקיות הענן הגדולות עצמן.

גם במוצרים שמאפשרים לך להריץ את השירות on-prem (או לחלופין בהתקנה עצמאית על תשתית IaaS בספקית הענן), יש נטייה לא פעם למורכבות גדולה מהרצוי, שבאה לידי ביטוי בד"כ בצורך להתקין קומפוננטות רבות שמשמשות את החלקים השונים במוצר, והרבה פעמים גם לנהל את ה- scaling שלהן בצורה בלתי תלוייה.

ב- ClickHouse המצב פשוט: כדי להריץ ClickHouse על node יחיד, צריך רק את ה- executable של CH ושום דבר מעבר. כדי להריץ את CH בתצורה של cluster, צריך את ה- node-ים עצמם ואת ClickHouse Keeper (מוצר תואם Zookeeper שפותח ע"י CH כדי להחליף את CH ולספק ביצועים טובים יותר) שיכול לרוץ כסרביס עצמאי, או כחלק מה-executable של ClickHouse עצמו.

גם התחזוקה של CH קלה למדי: בדומה להרבה מה-DB-ים ה-SQL-יים האחרים הוא חושף את המידע הפנימי שלו (מטריקות, לוגים וכו') בתור טבלאות, מה שמאפשר לתשאל אותן בקלות, לייצר דשבורדים לטובת ניטור וכו'.

הוא חוסך ETL-ים, גם פנימית וגם מול מידע חיצוני

כל מי שעובד בתחום ה- data מכיר את הצורך של לתת מענה לבעיות בעולם ה- ETL. בד"כ הן מתחלקות לכמה קטגוריות:

  • להכניס מידע פנימה ממקורות חיצוניים
  • להוציא מידע החוצה למקורות חיצוניים
  • להריץ תהליכים סיכומיים כאלה ואחרים

ClickHouse כולל באופן מובנה table engines שמאפשרים לייצר טבלאות שבהן המידע לא מאוחסן באמת ב- ClickHouse, אלא מייצג מידע שנמצא במקום אחר: Topic ב- Kafka, תור ב- RabbitMQ, טבלה ב- MSSQL או כל DB חיצוני אחר שנגיש עם ODBC ועוד.
השימוש בטבלאות האלה ביחד עם materialized views, או בפונקציות השונות שמאפשרות לתשאל מידע שנמצא במקורות חיצוניים, מקל מאד להכניס ולהוציא מידע מ-ClickHouse בצורה אוטומטית, ובלי להידרש לכלים חיצוניים שנמצאים מחוץ למוצר (על אף שאם אתם מעוניינים, כן יש לו שפע של integrations גם למוצרים כדוגמת Kafka Connect ואחרים).

גם כלפי חוץ ClickHouse קל לתשאול: יש אינטגרציה עם מוצרים רבים (שרק הולכת וגדלה). אישית, יוצא לי להשתמש לא מעט ב-ODBC driver של ClickHouse ל- windows, שמאפשר להגדיר linked-server מ-MSSQL ל- ClickHouse (וכך לעשות שליפות שמתחילות ב- MSSQL אך ניגשות למידע ששמור ב-CH). זה מאד נוח, במיוחד אם יש לכם כבר בהווה שימוש במוצרים נוספים (על אף שהייתי שמח אם ה- ODBC driver הזה היה יציב יותר, נתקלתי בו בלא מעט קריסות ובאגים קטנים).

גם לייצור של טבלאות סיכומיות או מניפלציות שונות של המידע יש ל- ClickHouse פתרון, שיבוא בד"כ בצורה של שילוב בין Materialized-views של ClickHouse (שהם משהו קצת שונה ממה שהמונח הזה אומר בתשתיות DB אחרות) ואחד מה-"טעמים" האחרים שיש ל-MergeTree (הפורמט הטבלאי העיקרי ב-CH) שמאפשר לשלוט באיך נשמר המידע (גרסה אחת בלבד עבור איזשהו tuple של ערכים, שמירת המידע עם אגרגציות שמחושבות חלקית מראש ועוד).

בתקופה האחרונה מוכנסים ל-CH גם פיצ'רים רבים שמאפשרים לו לעבוד בצורה טובה יותר מול object storage של שחקניות אחרות (לא רק S3) ובצורה יעילה יותר מול פורמטים שונים של טבלאות (Parquet, Delta-lake, Iceberg ועוד) מה שמאפשר להשתמש בו גם בתור query engine מעל data lake כלשהו, עם או בלי קשר לשאלה של האם באיזשהו שלב המידע מאוחסן ב- ClickHouse עצמו.

ניתן להפריד בין Compute ל-Storage

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

היכולת לשמור טבלאות MergeTree ב- S3 ב- CH היא חלק אינטגרלי במוצר, אבל חלק מהיכולות המתקדמות יותר שפותחו ע"י CH לטובת הפרדה בין compute ל-storage, קיימות רק בגרסת הענן (ולא חילחלו לגרסת ה- open-source שניתן להוריד ולהתקין עצמאית). דוגמא לכך זה ה-SharedMergeTree שמאפשר לשמור את המידע ב-object storage פעם אחת, במקום לשכפל אותו לפי כמות הרפליקות שיש בכל shard.

חשוב להדגיש שהשימוש ב- S3, או בכל object storage אחר אינו חובה ב-CH. אם אתם מעדיפים משיקולי תפעול, ביצועים, נוחות (או כל סיבה אחרת) לשמור את כלל המידע על דיסקים לוקאליים – אין שום דבר שעוצר אתכם.

הוא מתפתח מהר.

כמעט כל חודש יוצאת גרסה חדשה של ClickHouse, ומדובר לא פעם בגרסאות שכוללות פיצ'רים משמעותיים וחדשים. למעשה, מספיק לעבור על ה- pull requests ב- GitHub של CH כדי להתרשם מכמות הפיצ'רים שנכנסת (הן ע"י צוות הפיתוח של המוצר שעובד ב- CH עצמו, והן ע"י הקהילה).

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

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

אגב, אם אתם רוצים להישאר עם אצבע על הדופק לגבי הפיצ'רים החדשים ב- CH, מומלץ מאד לעבור על מסמך ה- changelog שלהם ולצפות בהצגה של הפיצ'רים החדשים שמתבצעת אחת לחודש ע"י אלכסיי מילובידוב (ה-CTO של ClickHouse) ביוטיוב.

אבל יש גם חסרונות

כמו כל דבר, גם השימוש ב- CH לא נטול חסרונות, ועל אף היתרונות הרבים שמניתי שבגללם אני חושב שמדובר במוצר מעולה ואני משתמש בו לא מעט – יש גם דברים פחות מוצלחים:

  • CH לא מתמודד טוב במיוחד עם שליפות שדורשות הרבה מאד זיכרון
    אם תנסו לעשות JOIN-ים ענקיים (שבכל אחד מהצדדים יש הרבה מאד שורות), או תנסו לעשות GROUP BY על שילוב עמודות עם high cardinality, אתם תגלו שזה יכול להיות סיפור די כואב, ופה הביצועים של CH נעשים פחות נפלאים. צריך להגיד בכנות שמדובר בבעייה די קשה, ו-CH עובדים כדי לשפר אספקטים שונים של הבעייה הזאת (למשל: ע"י יצירה של אלגוריתמים חדשים ל-JOIN כדוגמת parallel hash)
  • חלק מהגרסאות החדשות של CH שיוצאות מכילות רגרסיות/באגים חמורים מכפי שהייתם מצפים
    כאמור, מדובר בצד השני של המטבע של מדיניות שחרור הגרסאות התכופה. לפעמים נכנסים באגים או רגרסיות שבתור משתמש במוצר, היית ממש מעדיף שלא ייכנסו. בפיצ'רים חדשים זה בולט במיוחד, לא פעם הם נכנסים כשהם לא בוגרים מספיק, ובבירור לא נבדקו מול כל מטריצת האפשרויות ש-ClickHouse נותן לקנפג ואפילו לא קרוב לזה.
  • קיימות כל מיני מגבלות תפעוליות, לא פורמליות, שעדיף היה להסיר
    למשל, כל ReplicatedMergeTree מגיע עם overhead מסויים כדי לנהל את הסנכרון מול ה- zookeeper ולהביא מידע חדש שנכנס ל-replicas אחרים באותו ה- shard. זה גורם לכך שקיימת מגבלה אפקטיבית על כמות הטבלאות מהסוג הזה שאפשר להחזיק, לפני שרואים עלייה משמעותית בצריכת ה- CPU ובזמן שלוקח למידע חדש להיות נגיש.
  • התיעוד טוב, אבל לא מספיק
    יש ל-CH תיעוד, אפילו לא רע. אבל, נתקלתי כבר במצבים שבהם זכרתי שראיתי pull-request שקשור לפיצ'ר מסויים, או משהו שהופיע ב- change log ולא הוזכר בתיעוד, והייתי צריך להסתכל בקוד או בטסטים כדי להבין בדיוק איך אני משתמש בפיצ'ר הזה. במוצר שיש לו הרבה תורמים, ונכנס הרבה קוד, כנראה לא פשוט לשמור על התיעוד מקיף, עדכני אך גם לא מעיק ומבלבל. אבל, בלי זה, יכולים להיוןת כל מיני פיצ'רים שאמנם שימושיים אבל פשוט הולכים לאיבוד.

כדאי לכם לנסות

קל מאד להתחיל להשתמש ב- ClickHouse ולבדוק האם הוא מתאים ל- use-case-ים שיש לכם. ניתן להוריד את ה- docker images ולהריץ לוקאלית node יחיד בקלות (והוא מספיק חזק כדי להחזיק כנראה משחקים התחלתיים).
אם אתם רוצים להתנסות עם cluster מלא, ב- repo הזה יש קבצי docker compose שיאפשרו לכם להרים בקלות סביבה לוקאלית.

בהצלחה!

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *