الانتقال إلى المحتوى الرئيسي

ما هي عمليات دمج الأجزاء في ClickHouse؟


يتميّز ClickHouse بالسرعة ليس فقط في الاستعلامات، بل أيضًا في عمليات الإدراج، وذلك بفضل طبقة التخزين التي تعمل بأسلوب مشابه لـ LSM trees: ① تؤدي عمليات الإدراج (في الجداول من عائلة MergeTree engine) إلى إنشاء أجزاء بيانات مرتبة وغير قابلة للتغيير. ② تُنقَل كل معالجة البيانات إلى عمليات دمج الأجزاء في الخلفية. وهذا يجعل عمليات كتابة البيانات خفيفة وعالية الكفاءة. وللتحكم في عدد الأجزاء لكل جدول وتنفيذ ② أعلاه، يدمج ClickHouse باستمرار الأجزاء الأصغر في أجزاء أكبر في الخلفية (ضمن كل partition) إلى أن تصل إلى حجم مضغوط يقارب ~150 GB. يوضح المخطط التالي بإيجاز عملية الدمج في الخلفية:
تزداد قيمة merge level للجزء بمقدار واحد مع كل عملية دمج إضافية. ويعني المستوى 0 أن الجزء جديد ولم يُدمج بعد. أما الأجزاء التي دُمجت في أجزاء أكبر فتُعلَّم على أنها غير نشطة ثم تُحذف نهائيًا بعد مدة قابلة للتهيئة (8 دقائق افتراضيًا). ومع مرور الوقت، يؤدي ذلك إلى تكوين شجرة من الأجزاء المدمجة. ومن هنا جاءت تسمية جدول MergeTree.

مراقبة عمليات الدمج

في مثال ما هي أجزاء الجدول، بيّنا أن ClickHouse يتتبع جميع أجزاء الجدول في جدول النظام parts. واستخدمنا الاستعلام التالي لاسترجاع مستوى الدمج وعدد الصفوف المخزنة لكل جزء نشط في جدول المثال:
SELECT
    name,
    level,
    rows
FROM system.parts
WHERE (database = 'uk') AND (`table` = 'uk_price_paid_simple') AND active
ORDER BY name ASC;
تُظهر نتيجة الاستعلام الموثَّق سابقًا أن جدول المثال كان يحتوي على أربعة أجزاء نشطة، تكوَّن كلٌّ منها من عملية دمج واحدة للأجزاء التي أُدخلت في البداية:
   ┌─name────────┬─level─┬────rows─┐
1. │ all_0_5_1   │     1 │ 6368414 │
2. │ all_12_17_1 │     1 │ 6442494 │
3. │ all_18_23_1 │     1 │ 5977762 │
4. │ all_6_11_1  │     1 │ 6459763 │
   └─────────────┴───────┴─────────┘
عند تشغيل الاستعلام، يتضح الآن أن الأجزاء الأربعة قد اندمجت في جزء نهائي واحد (ما لم تجرِ عمليات insert إضافية إلى الجدول):
   ┌─name───────┬─level─┬─────rows─┐
1. │ all_0_23_2 │     2 │ 25248433 │
   └────────────┴───────┴──────────┘
في ClickHouse 24.10، أُضيفت لوحة معلومات لعمليات الدمج جديدة إلى لوحات معلومات المراقبة المضمنة. وهي متاحة في كلٍّ من OSS وCloud عبر معالج HTTP ‏/merges، ويمكن استخدامها لتصوير جميع عمليات دمج الأجزاء لجدولنا التجريبي:
تُظهر لوحة المعلومات المسجَّلة أعلاه العملية كاملة، بدءًا من عمليات إدراج البيانات الأولية وصولًا إلى الدمج النهائي في جزء واحد: ① عدد الأجزاء النشطة. ② عمليات دمج الأجزاء، ممثَّلة بصريًا بمربعات (يعكس الحجم حجم الجزء). تضخيم الكتابة.

عمليات دمج متزامنة

يستخدم خادم ClickHouse عدة خيوط دمج في الخلفية لتنفيذ عمليات دمج الأجزاء بشكل متزامن:
ينفّذ كل خيط دمج حلقةً على النحو التالي: ① يحدّد الأجزاء التي ستُدمج بعد ذلك، ويحمّلها إلى الذاكرة. ② يدمج الأجزاء الموجودة في الذاكرة في جزء أكبر. ③ يكتب الجزء المدمج إلى القرص. ثم ارجع إلى ① لاحظ أن زيادة عدد أنوية CPU وحجم RAM تتيح زيادة إنتاجية عمليات الدمج في الخلفية.

عمليات الدمج الموفّرة للذاكرة

لا يحمّل ClickHouse بالضرورة جميع الأجزاء المطلوب دمجها إلى الذاكرة دفعةً واحدة، كما هو موضّح في المثال السابق. واستنادًا إلى عدة عوامل، ولتقليل استهلاك الذاكرة (على حساب سرعة الدمج)، فإن ما يُسمّى الدمج العمودي يحمّل الأجزاء ويُجري دمجها على دفعات من الكتل بدلًا من تنفيذ ذلك دفعةً واحدة.

آليات الدمج

يوضح المخطط أدناه كيف يقوم خيط دمج واحد يعمل في الخلفية في ClickHouse بدمج الأجزاء (افتراضيًا، من دون الدمج العمودي):
تُنفَّذ عملية دمج الأجزاء على عدة خطوات: ① فك الضغط والتحميل: تُفك ضغط ملفات الأعمدة الثنائية المضغوطة الخاصة بالأجزاء المراد دمجها وتُحمَّل إلى الذاكرة. ② الدمج: تُدمَج البيانات في ملفات أعمدة أكبر. ③ الفهرسة: يُنشأ فهرس أساسي متناثر جديد لملفات الأعمدة المدمجة. ④ الضغط والتخزين: تُضغط ملفات الأعمدة والفهرس الجديدان ويُحفَظان في دليل جديد يمثل جزء البيانات المدمج. كما يُعاد أيضًا إنشاء البيانات الوصفية الإضافية في أجزاء البيانات، مثل فهارس تخطي البيانات الثانوية، وإحصاءات الأعمدة، والمجاميع الاختبارية، وفهارس الحدين الأدنى والأقصى، استنادًا إلى ملفات الأعمدة المدمجة. وقد أغفلنا هذه التفاصيل للتبسيط. تعتمد آلية الخطوة ② على محرك MergeTree المستخدم، إذ تتعامل المحركات المختلفة مع الدمج بطرق مختلفة. فعلى سبيل المثال، قد تُجمَّع الصفوف أو تُستبدل إذا كانت قديمة. وكما ذكرنا سابقًا، فإن هذا النهج يُسنِد جميع عمليات معالجة البيانات إلى عمليات الدمج في الخلفية، مما يتيح عمليات إدراج فائقة السرعة عبر إبقاء عمليات الكتابة خفيفة وفعّالة. بعد ذلك، سنعرض بإيجاز آليات الدمج في محركات محددة ضمن عائلة MergeTree.

عمليات الدمج القياسية

يوضح المخطط أدناه كيفية دمج الأجزاء في جدول MergeTree من النوع القياسي:
تنشئ عبارة DDL في المخطط أعلاه جدول MergeTree بمفتاح فرز (town, street)، ما يعني أن البيانات على القرص تُرتَّب وفقًا لهذه الأعمدة، ويُنشأ تبعًا لذلك فهرس أساسي متناثر. تُدمَج ② أعمدة الجدول ① المفكوكة الضغط والمرتبة مسبقًا مع الحفاظ على الترتيب العام للفرز في الجدول كما يحدده مفتاح الفرز، ويُنشأ ③ فهرس أساسي متناثر جديد، ثم تُضغط ④ ملفات الأعمدة المدمجة والفهرس وتُخزَّن كجزء بيانات جديد على القرص.

عمليات دمج ReplacingMergeTree

تعمل عمليات دمج الأجزاء في جدول ReplacingMergeTree بصورة مشابهة لـ عمليات الدمج القياسية، لكن لا يُحتفَظ إلا بأحدث إصدار من كل صف، بينما تُهمَل الإصدارات الأقدم:
تنشئ عبارة DDL في المخطط أعلاه جدول ReplacingMergeTree بمفتاح فرز (town, street, id)، ما يعني أن البيانات على القرص تُرتَّب بحسب هذه الأعمدة، ويُنشأ تبعًا لذلك فهرس أساسي متناثر. وتعمل عملية الدمج ② بطريقة مشابهة لما يحدث في جدول MergeTree قياسي، إذ تجمع الأعمدة المفكوك ضغطها والمفروزة مسبقًا مع الحفاظ على ترتيب الفرز العام. ومع ذلك، يزيل ReplacingMergeTree الصفوف المكررة التي لها مفتاح الفرز نفسه، ولا يُبقي إلا على أحدث صف استنادًا إلى الطابع الزمني لإنشاء الجزء الذي يحتويه.

عمليات الدمج بالتجميع

تُلخَّص البيانات الرقمية تلقائيًا أثناء دمج الأجزاء في جدول SummingMergeTree:
تُعرِّف عبارة DDL في المخطط أعلاه جدول SummingMergeTree بحيث يكون town مفتاح الفرز، ما يعني أن البيانات على القرص تُرتَّب وفقًا لهذا العمود، ويُنشأ تبعًا لذلك فهرس أساسي متناثر. في خطوة الدمج ②، يستبدل ClickHouse جميع الصفوف التي لها مفتاح الفرز نفسه بصف واحد، مع جمع قيم الأعمدة الرقمية.

عمليات الدمج التجميعية

يُعد مثال جدول SummingMergeTree الوارد أعلاه صيغة متخصصة من جدول AggregatingMergeTree، ويتيح التحويل التلقائي التزايدي للبيانات من خلال تطبيق أيٍ من دوال التجميع البالغ عددها [90+] أثناء عمليات دمج الأجزاء:
تُنشئ عبارة DDL في المخطط أعلاه جدول AggregatingMergeTree مع town بوصفه مفتاح الفرز، مما يضمن ترتيب البيانات حسب هذا العمود على القرص وإنشاء فهرس أساسي متناثر مطابق. أثناء ② الدمج، يستبدل ClickHouse جميع الصفوف التي لها مفتاح الفرز نفسه بصف واحد يخزّن حالات التجميع الجزئية (مثل sum وcount من أجل avg()). وتضمن هذه الحالات نتائج دقيقة من خلال عمليات الدمج التزايدية في الخلفية.
آخر تعديل في ٢٩ يونيو ٢٠٢٦