تشغيل ClickHouse في بيئة الإنتاج: الإدخال والدمج والتكلفة عند ملياري صف
تجربتنا في تشغيل ClickHouse في الإنتاج: تصميم المخطّط وتحسين الاستعلامات وكيف نحقّق تحليلات دون الثانية عبر أكثر من 100 مليون حدث جهاز.
عندما بدأنا ببناء طبقة التحليلات في tracio.ai، احتجنا إلى قاعدة بيانات قادرة على التعامل مع عبء العمل الخاص بنا: إدخال 50٬000 حدث تعريف جهاز في الثانية، وتخزين أكثر من ملياري صف، والإجابة عن الاستعلامات التحليلية في أقل من ثانية واحدة. قيّمنا PostgreSQL (بطيئة جداً للتجميعات عند هذا الحجم)، وElasticsearch (باهظة جداً لتحليلات السلاسل الزمنية)، وClickHouse. فازت ClickHouse بشكل حاسم.
لماذا ClickHouse
ClickHouse هي قاعدة بيانات OLAP عمودية التوجّه مُصمّمة للتحليلات في الزمن الحقيقي. ميزتها الأساسية لعبء عملنا هي أنها تقرأ فقط الأعمدة اللازمة لكل استعلام. عندما يسأل محلّل احتيال «أرِني معدّل الاحتيال حسب الدولة خلال الأيام السبعة الماضية»، تقرأ ClickHouse أعمدة الدولة والطابع الزمني وrisk_score فقط — متجاهلةً الأعمدة الأربعين وأكثر الأخرى في جدول الأحداث. على جدول من ملياري صف، يقلّل هذا الإدخال/الإخراج بنسبة 95%.
كما تضغط ClickHouse البيانات بشكل ممتاز. يشغل جدول الأحداث لدينا ذو الملياري صف 340 غيغابايت على القرص — نحو 170 بايت لكل صف مضغوط، مقابل 1.2 كيلوبايت لكل صف غير مضغوط. نسبة الضغط البالغة 7:1 تعني أن مزيداً من البيانات يتّسع في الذاكرة، وهو ما يُترجَم مباشرةً إلى استعلامات أسرع.
تصميم المخطّط
يخزّن جدولنا الأساسي صفاً واحداً لكل حدث تعريف:
يستخدم الجدول محرّك MergeTree، مُرتَّباً حسب (workspace_id, toDate(timestamp), visitor_hash). هذا الترتيب حاسم — فهو يعني أن الاستعلامات المُرشَّحة حسب مساحة العمل والمدى الزمني تقرأ أقل قدر من البيانات. ويتيح عمود visitor_hash عمليات بحث سريعة حسب معرّف الزائر دون فهرس ثانوي.
اخترنا LowCardinality(String) للأعمدة country وdevice_type وbrowser_family وos_family لأن هذه الأعمدة تحتوي على أقل من 10٬000 قيمة مميّزة. تخزّن ClickHouse أعمدة LowCardinality كأعداد صحيحة مُرمَّزة بقاموس، ما يقلّل التخزين بنسبة 80% مقارنةً بالسلاسل النصية العادية ويسرّع عمليات GROUP BY.
استراتيجية التقسيم الأفقي
نقسّم جدول الأحداث أفقياً عبر 6 عُقد باستخدام تجزئة لـworkspace_id. يضمن هذا أن جميع أحداث عميل معيّن تكون على القسم نفسه، ما يعني أن معظم الاستعلامات (المُرشَّحة حسب workspace_id) تصيب قسماً واحداً. ولا تلزم الاستعلامات العابرة للأقسام إلا للتحليلات الداخلية.
يمتلك كل قسم نسختين مكرّرتين لتوفير التوافر العالي. يستخدم النسخ المتماثل محرّك ReplicatedMergeTree المدمج في ClickHouse مع تنسيق ZooKeeper. تجاوز الأعطال تلقائي — إذا تعطّل قسم، تُوجَّه الاستعلامات إلى النسخة المكرّرة دون أي تغييرات من جانب العميل.
خط أنابيب الإدخال
تتدفّق الأحداث من موضوع Kafka لدينا إلى ClickHouse عبر خدمة Go مخصّصة تُجمّع الإدخالات. نُدخِل بدفعات من 10٬000 صف كل 500 مللي ثانية — يوازن هذا بين زمن استجابة الإدخال (دون الثانية) وكفاءة الإدخال (تؤدّي ClickHouse على أفضل نحو مع الدفعات الكبيرة).
تتعامل خدمة الإدخال مع الضغط العكسي بسلاسة. إذا كانت ClickHouse بطيئة في قبول الإدخالات (أثناء عمليات الدمج أو الحمل الثقيل للاستعلامات)، تخزّن الخدمة حتى مليون حدث في الذاكرة وتطبّق ضغطاً عكسياً على مستهلك Kafka. خلال 18 شهراً من الإنتاج، لم نفقد أي حدث على الإطلاق.
تحسين الاستعلامات
العروض المُجسّدة
لاستعلامات لوحات المعلومات الشائعة، نستخدم عروضاً مُجسّدة تُجمّع البيانات مسبقاً. تقرأ لوحة معدّل الاحتيال لدينا، مثلاً، من عرض مُجسّد يُجمّع أعداد fraud_detected حسب مساحة العمل والدولة والساعة. يقلّل العرض البيانات المفحوصة لهذا الاستعلام من ملياري صف إلى 5 ملايين صف.
ترتيب الإسقاطات
تتيح لنا إسقاطات ClickHouse تعريف ترتيبات فرز بديلة لجدول دون تكرار البيانات. أضفنا إسقاطاً مُرتَّباً حسب (workspace_id, visitor_hash, timestamp) لاستعلامات المخطّط الزمني للزائر. من دون الإسقاط، كانت هذه الاستعلامات تفحص مدى تواريخ كاملة. ومعه، تقرأ فقط الكتل التي تحتوي على الزائر المستهدف.
الدوال التقريبية
للاستعلامات التي لا تكون فيها الأعداد الدقيقة حرجة، نستخدم دوال ClickHouse التقريبية: uniqCombined للأعداد المميّزة (هامش خطأ 2%، أسرع 10 أضعاف من uniqExact) وquantileTDigest لحسابات النسب المئوية. تستخدم لوحة تحليلات الاحتيال الدوال التقريبية حصراً، ما يُبقي جميع استعلامات لوحة المعلومات دون 200 مللي ثانية.
أرقام الأداء
فيما يلي معايير أداء تمثيلية للاستعلامات على عنقود الإنتاج لدينا ذي الملياري صف:
معدّل الاحتيال حسب الدولة، آخر 7 أيام: 120 مللي ثانية. المخطّط الزمني للزائر (50 حدثاً): 8 مللي ثانية. الزوّار الفريدون يومياً، آخر 30 يوماً: 340 مللي ثانية. توزيع درجة المخاطرة، آخر 24 ساعة: 95 مللي ثانية. أعلى 100 جهاز حسب عدد الأحداث، آخر 30 يوماً: 210 مللي ثانية.
تتضمّن هذه الأرقام رحلة الشبكة ذهاباً وإياباً من خوادم تطبيقنا إلى عنقود ClickHouse. أما زمن تنفيذ الاستعلام الصافي فيكون عادةً أقل بنسبة 30–50%.
دروس تشغيلية
الدرس الأول: راقب تأخّر الدمج
يدمج محرّك MergeTree في ClickHouse قطع البيانات الصغيرة باستمرار في قطع أكبر. إذا تأخّرت عمليات الدمج (بسبب ارتفاع معدّل الإدخال أو تنافس الإدخال/الإخراج على القرص)، يتدهور أداء الاستعلامات لأنها يجب أن تفحص مزيداً من القطع. نراقب عدد القطع لكل تقسيمة وننبّه عندما يتجاوز 300.
الدرس الثاني: تجنّب عمليات ALTER TABLE الكبيرة
إضافة عمود إلى جدول من ملياري صف في ClickHouse فورية (فهي على مستوى البيانات الوصفية فقط). لكن تغيير نوع عمود يتطلّب إعادة كتابة جميع قطع البيانات — عملية استغرقت 6 ساعات على عنقودنا. نتعامل الآن مع المخطّط على أنه للإلحاق فقط: تُضاف الأعمدة الجديدة بحرّية، لكن تغييرات النوع تمرّ عبر جدول ترحيل.
الدرس الثالث: TTL بحذر
تدعم ClickHouse انتهاء صلاحية البيانات تلقائياً عبر TTL. ضبطنا TTL بمدة 90 يوماً على جدول الأحداث لدينا. المزلق: يحدث حذف TTL أثناء عمليات الدمج، ما يعني أن البيانات المحذوفة قد تبقى ساعات أو أياماً بعد انتهاء صلاحية TTL. للحذف الحرج للامتثال، نشغّل استعلامات ALTER TABLE DELETE صريحة وفق جدول زمني.
التكلفة
يكلّف عنقود ClickHouse لدينا ذو الـ6 عُقد (كل عقدة: 32 vCPU، و128 غيغابايت RAM، و2 تيرابايت NVMe) نحو 8٬400 دولار شهرياً على استضافة الخوادم الفعلية. يخزّن هذا ملياري صف مع احتفاظ لمدة 90 يوماً ويتعامل مع 50 ألف إدخال/ثانية إضافةً إلى 200 استعلام لوحة معلومات متزامن. تكلفة الحدث المُخزَّن الواحد 0.0000042 دولار — أرخص بمراتب من التحليلات المماثلة على قواعد البيانات السحابية المُدارة.