Skip to main content
تتضمن عمليات نشر ClickHouse لأغراض Observability دائمًا مجموعات بيانات كبيرة تحتاج إلى إدارة. ويوفّر ClickHouse عددًا من الميزات التي تساعد على إدارة البيانات.
يأتي ClickStack بمخطط افتراضي مُحسَّنيوفّر ClickStack مخططات جاهزة للسجلات والتتبعات والمقاييس تتضمن أحدث ميزات ClickHouse (فهارس نصية للبحث النصي الكامل والبحث في مفاتيح map، وأعمدة materialized ومصفوفات ALIAS للتصفية بالقراءة المباشرة، وعمليات lookup للصفوف حسب رقم block)، وقد خضعت لاختبارات benchmark لتقديم أداء قوي جاهزًا للاستخدام لأعباء عمل التسجيل والتتبعات. استخدمها كنقطة مرجعية لتصميمك الخاص.

الأقسام

يتيح التقسيم في ClickHouse فصل البيانات منطقيًا على القرص وفقًا لعمود أو تعبير SQL. ومن خلال هذا الفصل المنطقي للبيانات، يمكن التعامل مع كل قسم على حدة، مثل حذفه. ويتيح لك ذلك نقل الأقسام، وبالتالي المجموعات الفرعية، بين طبقات التخزين بكفاءة استنادًا إلى الوقت أو انتهاء صلاحية البيانات/الحذف بكفاءة من cluster. يُحدَّد التقسيم في الجدول عند تعريفه لأول مرة عبر عبارة PARTITION BY. ويمكن أن تتضمن هذه العبارة تعبير SQL على أي عمود أو أعمدة، وتحدِّد نتائج هذا التعبير القسم الذي يُرسَل إليه الصف. ترتبط أجزاء البيانات منطقيًا (من خلال بادئة مشتركة لاسم المجلد) بكل قسم على القرص، ويمكن الاستعلام عنها بشكل منفصل. في المثال أدناه، يُقسِّم مخطط otel_logs الافتراضي البيانات حسب اليوم باستخدام التعبير toDate(Timestamp). وعند إدراج الصفوف في ClickHouse، سيُقيَّم هذا التعبير على كل صف ويُوجَّه إلى القسم الناتج إذا كان موجودًا (وإذا كان هذا الصف هو الأول لذلك اليوم، فسيُنشأ القسم).
يمكن تنفيذ عدد من العمليات على الأقسام، بما في ذلك النسخ الاحتياطية، ومعالجة الأعمدة، وعمليات التغيير التي تعدّل/تحذف البيانات صفًا بصف) ومسح الفهارس (مثل الفهارس الثانوية). على سبيل المثال، لنفترض أن جدول otel_logs لدينا مُقسَّم حسب اليوم. وإذا جرى ملؤه بمجموعة بيانات السجلات المنظَّمة، فسيحتوي على بيانات تمتد لعدة أيام:
يمكن العثور على الأقسام الحالية باستخدام استعلام بسيط على أحد جداول النظام:
قد يكون لدينا جدول آخر، otel_logs_archive، نستخدمه لتخزين البيانات الأقدم. ويمكن نقل البيانات إلى هذا الجدول بكفاءة على مستوى الأقسام (إذ إن هذا مجرد تغيير في البيانات الوصفية).
وهذا بخلاف التقنيات الأخرى التي تتطلب استخدام INSERT INTO SELECT وإعادة كتابة البيانات في الجدول الهدف الجديد.
نقل الأقساميتطلب نقل الأقسام بين الجداول استيفاء عدة شروط، من أبرزها أن تتطابق الجداول في البنية ومفتاح التقسيم والمفتاح الأساسي والفهارس/الإسقاطات. ويمكن العثور هنا على ملاحظات مفصلة حول كيفية تحديد الأقسام في ALTER DDL.
وعلاوة على ذلك، يمكن حذف البيانات بكفاءة بحسب القسم. وهذا أكثر كفاءةً بكثير من حيث استهلاك الموارد من التقنيات البديلة (mutations أو lightweight deletes)، لذا يُفضَّل اعتماده.
تُستغل هذه الميزة بواسطة TTL عند استخدام الإعداد ttl_only_drop_parts=1. راجع إدارة البيانات باستخدام TTL لمزيد من التفاصيل.

التطبيقات

يوضح ما سبق كيف يمكن نقل البيانات ومعالجتها بكفاءة على مستوى الأقسام. عمليًا، من المرجّح أنك ستستخدم عمليات الأقسام في حالات استخدام Observability غالبًا ضمن سيناريوهين:
  • المعماريات متعددة الطبقات - نقل البيانات بين طبقات التخزين (راجع طبقات التخزين)، مما يتيح بناء معماريات ساخنة-باردة.
  • الحذف الفعّال - عند وصول البيانات إلى قيمة TTL محددة (راجع إدارة البيانات باستخدام TTL)
نستعرض كلاهما بالتفصيل أدناه.

أداء الاستعلامات

مع أن التقسيمات قد تساعد في تحسين أداء الاستعلامات، فإن ذلك يعتمد بدرجة كبيرة على أنماط الوصول. فإذا كانت الاستعلامات تستهدف عددًا قليلًا فقط من التقسيمات (ويُفضَّل أن يكون قسمًا واحدًا)، فقد يتحسن الأداء. ويكون هذا مفيدًا عادةً فقط إذا لم يكن مفتاح القسم ضمن المفتاح الأساسي وكنت تُجري التصفية بناءً عليه. أما الاستعلامات التي تحتاج إلى شمول عدد كبير من التقسيمات، فقد يكون أداؤها أسوأ مما لو لم يُستخدم التقسيم أصلًا (إذ قد يكون هناك عدد أكبر من الأجزاء). كما أن فائدة استهداف قسم واحد ستكون أقل وضوحًا، أو قد تنعدم تمامًا، إذا كان مفتاح القسم مكوّنًا مبكرًا بالفعل في المفتاح الأساسي. ويمكن أيضًا استخدام التقسيم لتحسين استعلامات GROUP BY إذا كانت القيم في كل قسم فريدة. ومع ذلك، وبوجه عام، ينبغي التأكد أولًا من تحسين المفتاح الأساسي، وعدم اللجوء إلى التقسيم كتقنية لتحسين الاستعلامات إلا في حالات استثنائية تكون فيها أنماط الوصول موجَّهة إلى مجموعة فرعية محددة ويمكن التنبؤ بها من البيانات، مثل التقسيم حسب اليوم عندما تكون معظم الاستعلامات على اليوم الأخير. راجع هنا للاطلاع على مثال على هذا السلوك.

إدارة البيانات باستخدام TTL (Time-to-live)

تُعد ميزة Time-to-Live (TTL) عنصرًا أساسيًا في حلول Observability المعتمدة على ClickHouse لتحقيق إدارة فعّالة للبيانات والاحتفاظ بها، لا سيما مع التوليد المستمر لكميات هائلة من البيانات. ويؤدي تطبيق TTL في ClickHouse إلى انتهاء صلاحية البيانات الأقدم وحذفها تلقائيًا، بما يضمن الاستخدام الأمثل للتخزين والحفاظ على الأداء من دون تدخل يدوي. وتُعد هذه الإمكانية ضرورية للحفاظ على قاعدة بيانات خفيفة، وخفض تكاليف التخزين، والإبقاء على الاستعلامات سريعة وفعّالة عبر التركيز على البيانات الأحدث والأكثر صلة. كما تساعد على الامتثال لسياسات الاحتفاظ بالبيانات من خلال إدارة دورة حياة البيانات بصورة منهجية، مما يعزز الاستدامة العامة لحل Observability وقابليته للتوسع. يمكن تحديد TTL إما على مستوى الجدول أو على مستوى العمود في ClickHouse.

TTL على مستوى الجدول

يتضمن المخطط الافتراضي لكلٍّ من السجلات والتتبعات قيمة TTL بحيث تنتهي صلاحية البيانات بعد فترة محددة. ويُحدَّد ذلك في ClickHouse exporter تحت المفتاح ttl، على سبيل المثال.
تدعم هذه الصياغة حاليًا صياغة Duration في Golang. نوصي باستخدام h والتأكد من أن ذلك يتوافق مع فترة التقسيم. على سبيل المثال، إذا كان التقسيم حسب اليوم، فتأكد من أن القيمة تمثل مضاعفات الأيام، مثل 24h و48h و72h. وسيضمن ذلك تلقائيًا إضافة عبارة TTL إلى الجدول، على سبيل المثال إذا كانت ttl: 96h.
بشكل افتراضي، تُزال البيانات التي انتهت صلاحية TTL الخاصة بها عندما يقوم ClickHouse بدمج أجزاء البيانات. وعندما يكتشف ClickHouse أن البيانات انتهت صلاحيتها، فإنه يُجري عملية دمج خارج الجدول الزمني.
TTLs المجدولةلا تُطبَّق TTLs فورًا، بل وفق جدول زمني كما ذُكر أعلاه. يحدّد إعداد جدول MergeTree merge_with_ttl_timeout الحد الأدنى للتأخير، بالثواني، قبل تكرار عملية دمج مع delete TTL. القيمة الافتراضية هي 14400 ثانية (4 ساعات). لكن هذا مجرد الحد الأدنى للتأخير؛ فقد يستغرق الأمر وقتًا أطول قبل أن يتم تشغيل عملية دمج TTL. وإذا كانت القيمة منخفضة جدًا، فسيؤدي ذلك إلى تنفيذ العديد من عمليات الدمج خارج الجدول الزمني، ما قد يستهلك قدرًا كبيرًا من الموارد. ويمكن فرض انتهاء TTL باستخدام الأمر ALTER TABLE my_table MATERIALIZE TTL.
**مهم: نوصي باستخدام الإعداد ttl_only_drop_parts=1 ** (المطبّق بواسطة المخطط الافتراضي). عند تمكين هذا الإعداد، يحذف ClickHouse جزءًا كاملًا عندما تكون جميع الصفوف فيه منتهية الصلاحية. إن حذف الأجزاء كاملةً بدلًا من التنظيف الجزئي للصفوف التي انتهت صلاحية TTL الخاصة بها (وهو ما يتم عبر عمليات mutation كثيفة الاستهلاك للموارد عندما تكون قيمة ttl_only_drop_parts=0) يتيح استخدام قيم merge_with_ttl_timeout أقصر وتقليل التأثير في أداء النظام. وإذا كانت البيانات مُقسّمة وفق الوحدة نفسها التي تُطبَّق عندها صلاحية TTL، مثل اليوم، فستحتوي الأجزاء بطبيعتها على بيانات من interval المحدد فقط. وهذا يضمن إمكانية تطبيق ttl_only_drop_parts=1 بكفاءة.

TTL على مستوى العمود

ينتهي المثال أعلاه صلاحية البيانات على مستوى الجدول. ويمكنك أيضًا إنهاء صلاحية البيانات على مستوى العمود. ومع تقادم البيانات، يمكن استخدام ذلك لحذف الأعمدة التي لا تبرر قيمتها في التحقيقات تكلفة الموارد الإضافية اللازمة للاحتفاظ بها. على سبيل المثال، نوصي بالاحتفاظ بالعمود Body تحسبًا لإضافة بيانات وصفية ديناميكية جديدة لم تُستخرج عند وقت الإدراج، مثل وسم Kubernetes جديد. وبعد فترة، مثل شهر واحد، قد يتضح أن هذه البيانات الوصفية الإضافية غير مفيدة، ما يقلل جدوى الاحتفاظ بالعمود Body. فيما يلي، نوضح كيف يمكن حذف العمود Body بعد 30 يومًا.
يتطلّب تحديد TTL على مستوى العمود من المستخدمين تعريف المخطط الخاص بهم بأنفسهم. ولا يمكن تحديده في OTel collector.

إعادة ضغط البيانات

مع أننا نوصي عادةً باستخدام ZSTD(1) لمجموعات بيانات observability، يمكنك تجربة خوارزميات ضغط مختلفة أو مستويات ضغط أعلى مثل ZSTD(3). وإلى جانب إمكانية تحديد ذلك عند إنشاء المخطط، يمكن أيضًا ضبط إعدادات الضغط بحيث تتغير بعد فترة زمنية محددة. وقد يكون هذا مناسبًا إذا كان codec أو خوارزمية الضغط يحسّن الضغط لكنه يؤدي إلى تراجع في أداء الاستعلام. قد تكون هذه المفاضلة مقبولة للبيانات الأقدم، التي يقل الاستعلام عنها، لكنها قد لا تكون مناسبة للبيانات الحديثة، التي تُستخدَم بوتيرة أكبر في عمليات التحقيق. يوضح المثال أدناه ذلك، حيث نضغط البيانات باستخدام ZSTD(3) بعد 4 أيام بدلًا من حذفها.
قيِّم الأداءنوصي المستخدمين دائمًا بتقييم أثر مستويات الضغط وخوارزمياته المختلفة على كلٍّ من أداء الإدراج وأداء الاستعلام. على سبيل المثال، قد تكون codecs من نوع delta مفيدة في ضغط الطوابع الزمنية. ومع ذلك، إذا كانت هذه جزءًا من المفتاح الأساسي، فقد يتراجع أداء التصفية.
يمكن العثور على مزيد من التفاصيل والأمثلة حول تهيئة TTL هنا. ويمكن العثور على أمثلة، مثل كيفية إضافة إعدادات TTL للجداول والأعمدة وتعديلها، هنا. ولمعرفة كيف تُمكّن إعدادات TTL طبقات تخزين متدرجة، مثل معماريات الساخن-الدافئ، راجع طبقات التخزين.

طبقات التخزين

في ClickHouse، يمكنك إنشاء طبقات تخزين على أقراص مختلفة، مثل تخزين البيانات الحديثة/الساخنة على SSD والبيانات الأقدم على تخزين مدعوم بـ S3. تتيح هذه المعمارية استخدام وسائط تخزين أقل تكلفة للبيانات الأقدم، والتي يمكن أن تكون لها اتفاقيات مستوى الخدمة الخاصة بالاستعلامات أعلى نظرًا لقلة استخدامها في التحقيقات.
غير ذي صلة بـ ClickHouse Cloudيستخدم ClickHouse Cloud نسخة واحدة من البيانات مخزنة على S3، مع ذواكر تخزين مؤقت على العقد مدعومة بـ SSD. لذلك، لا تكون طبقات التخزين مطلوبة في ClickHouse Cloud.
يتطلب إنشاء طبقات التخزين من المستخدمين أولًا إنشاء أقراص، ثم تُستخدم هذه الأقراص لوضع سياسات التخزين، مع وحدات تخزين يمكن تحديدها أثناء إنشاء الجدول. ويمكن نقل البيانات تلقائيًا بين الأقراص استنادًا إلى معدلات الامتلاء، وأحجام الأجزاء، وأولويات وحدات التخزين. يمكن العثور على مزيد من التفاصيل هنا. ومع أنه يمكن نقل البيانات يدويًا بين الأقراص باستخدام الأمر ALTER TABLE MOVE PARTITION، فإنه يمكن أيضًا التحكم في نقل البيانات بين وحدات التخزين باستخدام TTLs. يمكن العثور على مثال كامل هنا.

إدارة تغييرات المخططات

من المحتم أن تتغير مخططات السجلات والتتبعات على مدى عمر النظام، مثلًا عندما يراقب المستخدمون أنظمة جديدة لها بيانات وصفية أو تسميات جراب مختلفة. ومن خلال إنتاج البيانات باستخدام مخطط OTel، مع الاحتفاظ ببيانات الأحداث الأصلية بصيغة منظَّمة، ستكون مخططات ClickHouse أكثر قدرة على التكيف مع هذه التغييرات. ومع ذلك، مع توفر بيانات وصفية جديدة وتغير أنماط وصول الاستعلامات، ستحتاج إلى تحديث المخططات لتعكس هذه المستجدات. ولتجنب فترة التوقف أثناء تغييرات المخططات، تتوفر للمستخدمين عدة خيارات نعرضها أدناه.

استخدام القيم الافتراضية

يمكن إضافة الأعمدة إلى المخطط باستخدام قيم DEFAULT. ستُستخدم القيمة الافتراضية المحددة إذا لم تُحدَّد أثناء INSERT. يمكن إجراء تغييرات على المخطط قبل تعديل أي منطق تحويل في العرض المادي أو تهيئة OTel collector، ما يؤدي إلى إرسال هذه الأعمدة الجديدة. بمجرد تغيير المخطط، يمكنك إعادة تهيئة OTel collectors. على افتراض أن المستخدمين يتبعون العملية الموصى بها الموضحة في “استخراج البنية باستخدام SQL”، حيث ترسل OTel collectors بياناتها إلى محرك الجدول Null، ويتولى عرض مادي استخراج المخطط المستهدف وإرسال النتائج إلى الجدول المستهدف للتخزين، يمكن تعديل العرض باستخدام صيغة ALTER TABLE ... MODIFY QUERY syntax. لنفترض أن لدينا أدناه الجدول المستهدف مع العرض المادي المقابل له (المشابه لما استُخدم في “استخراج البنية باستخدام SQL”) لاستخراج المخطط المستهدف من السجلات المهيكلة الخاصة بـ OTel:
لنفترض أننا نريد استخراج عمود جديد Size من LogAttributes. يمكننا إضافة ذلك إلى المخطط باستخدام ALTER TABLE، مع تحديد القيمة الافتراضية:
في المثال أعلاه، نحدّد القيمة الافتراضية على أنها المفتاح size في LogAttributes (وستكون هذه القيمة 0 إذا لم يكن موجودًا). وهذا يعني أن الاستعلامات التي تصل إلى هذا العمود في الصفوف التي لم تُدرَج فيها هذه القيمة ستحتاج إلى الوصول إلى Map، وبالتالي ستكون أبطأ. ويمكننا أيضًا بسهولة تحديدها كثابت، مثل 0، مما يقلّل تكلفة الاستعلامات اللاحقة على الصفوف التي لا تحتوي على هذه القيمة. ويُظهر الاستعلام عن هذا الجدول أن القيمة قد مُلئت من Map كما هو متوقع:
لضمان إدراج هذه القيمة في كل البيانات المستقبلية، يمكننا تعديل العرض المادي باستخدام صيغة ALTER TABLE كما هو موضح أدناه:
ستتضمن الصفوف اللاحقة عمود Size يُعبَّأ عند الإدراج.

إنشاء جداول جديدة

كبديل للعملية المذكورة أعلاه، يمكنك ببساطة إنشاء الجدول المستهدف الجديد باستخدام المخطط الجديد. بعد ذلك، يمكن تعديل أي materialized views لاستخدام الجدول الجديد باستخدام ALTER TABLE MODIFY QUERY. المذكور أعلاه. وبهذا النهج، يمكنك إصدار نسخ من جداولك، مثل otel_logs_v3. يترك هذا النهج المستخدمين أمام عدة جداول للاستعلام عنها. وللاستعلام عبر عدة جداول، يمكنك استخدام الدالة merge، التي تقبل أنماط wildcard لاسم الجدول. نوضح ذلك أدناه من خلال الاستعلام عن الإصدارين v2 وv3 من جدول otel_logs:
إذا أراد المستخدمون تجنب استخدام الدالة merge وتوفير جدول للمستخدمين النهائيين يجمع بين عدة جداول، فيمكن استخدام محرك جدول Merge. نوضح ذلك أدناه:
يمكن تحديث ذلك كلما أُضيف جدول جديد باستخدام صياغة EXCHANGE للجدول. على سبيل المثال، لإضافة جدول بالإصدار v4، يمكننا إنشاء جدول جديد واستبداله ذرّيًا بالإصدار السابق.
آخر تعديل في ٢٣ يوليو ٢٠٢٦