בניית מפות חום (Heatmaps) לווידאו באמצעות HyperLogLog ב-Postgres
אתם רוצים להראות לעורכים בדיוק איפה הצופים מפסיקים לצפות בווידאו.
כדי לעשות זאת, אתם זקוקים למפת חום (heatmap). מפת חום זו מציגה צופים ייחודיים עבור כל שנייה בווידאו.
ב-TrendVidStream, אנחנו מעבדים 40 מיליון אירועים בכל יום. אנחנו משתמשים בסטאק פשוט: PHP 8.4 ו-Postgres.
הבעיה היא ה-cardinality. ספירת צופים ייחודיים לכל שנייה היא משימה קשה.
הדרך הנאיבית:
- שימוש בטבלת אירועים גולמית (raw events table).
- הרצת
COUNT(DISTINCT viewer_hash). - קיבוץ (Group by) לפי וידאו וחלון זמן (time bucket).
השיטה הזו נכשלה במהירות. עבור 90 ימי נתונים, אתם זקוקים ל-3.6 מיליארד שורות. השאילתות ארכו 9 שניות. בנוסף, ספירות ייחודיות לא ניתנות לסיכום (roll up). אי אפשר לסכום "ייחודיים לשעה" כדי לקבל "ייחודיים ליום" מכיוון שאותו אדם מופיע בשניהם.
הפתרון: HyperLogLog (HLL).
HLL יוצר "sketch" (סקיצה) קטן של סט הנתונים. הוא זעיר, בסביבות 10 KB, ללא קשר לכמות המיליונים שצופים בווידאו.
למה HLL עובד עבור מפות חום:
- גודל קבוע: הוא נשאר קטן.
- ניתן למיזוג (Mergeable): ניתן לבצע union לשתי סקיצות כדי לקבל ספירה גלובלית. זה פותר את בעיית ה-rollup.
המימוש שלנו:
אנחנו משתמשים בתוסף postgresql-hll. אנחנו משתמשים בשתי טבלאות.
- טבלת staging: טבלה מסוג append-only שבה PHP כותב heartbeats גולמיים. אנחנו לא משתמשים באינדקסים כאן כדי לשמור על כתיבה מהירה.
- טבלת sketch: זו שומרת את נתוני ה-HLL.
אנחנו משתמשים ב-cron job כדי להעביר נתונים מטבלת ה-staging לטבלת ה-sketch. אנחנו משתמשים בפקודה הזו:
INSERT INTO video_heatmap (...) SELECT ..., hll_add_agg(hll_hash_text(viewer_hash), 14, 5) FROM beacon_staging GROUP BY ... ON CONFLICT (...) DO UPDATE SET viewers = video_heatmap.viewers || EXCLUDED.viewers;
האופרטור "||" ממזג את הנתונים החדשים לתוך ה-sketch הקיים.
התוצאות:
- מהירות שאילתות: שאילתות מפת החום שלנו אורכות בין 15 ל-40 מילי-שניות (ms). הדרך הישנה ארכה 9 שניות.
- אחסון: סט הנתונים שלנו ל-90 יום הוא 40 GB. הדרך הישנה הייתה דורשת 1.2 TB.
- דיוק: אנחנו רואים שגיאה של כ-0.6% עד 1.4%. עבור מפת חום, זה מושלם.
לקחים שנלמדו:
- בצעו hashing בתוך מסד הנתונים. אל תבצעו hashing באפליקציה שלכם.
- בצעו עדכונים ב-batch. אל תעדכנו sketches שורה אחר שורה, אחרת תיצרו write hotspots.
- השתמשו ב-salt יומי עבור ה-hashes. זה מגן על הפרטיות ושומר על ה-sketches היומיים שלכם נקיים.
אם ה-dashboard שלכם איטי בגלל COUNT(DISTINCT), אתם לא זקוקים למחסן נתונים (data warehouse) עצום. אתם זקוקים ל-HLL.
