Skip to main content
يُعدّ المحرّك MergeTree وغيره من محركات عائلة MergeTree (مثل ReplacingMergeTree و AggregatingMergeTree) من أكثر محركات الجداول استخدامًا وأكثرها متانة في ClickHouse. صُمِّمت محركات الجداول من عائلة MergeTree لدعم معدلات عالية من إدخال البيانات وأحجام بيانات هائلة. وتُنشئ عمليات الإدخال أجزاءً للجدول، ثم تدمجها عملية تعمل في الخلفية مع أجزاء جدول أخرى. الميزات الرئيسية لمحركات الجداول من عائلة MergeTree.
  • يحدّد المفتاح الأساسي للجدول ترتيب الفرز داخل كل جزء من الجدول (فهرس عنقودي). كما أن المفتاح الأساسي لا يشير إلى صفوف فردية، بل إلى كتل من 8192 صفًا تُسمى حبيبات. وهذا يجعل المفاتيح الأساسية لمجموعات البيانات الضخمة صغيرة بما يكفي لتبقى محمّلة في الذاكرة الرئيسية، مع استمرار توفير وصول سريع إلى البيانات المخزّنة على القرص.
  • يمكن تقسيم الجداول باستخدام تعبير تقسيم عشوائي. ويضمن تقسيم pruning استبعاد الأقسام من القراءة عندما يتيح الاستعلام ذلك.
  • يمكن نسخ البيانات عبر عدة عقد في cluster لتحقيق التوفّر العالي، وfailover، والترقيات من دون downtime. راجع نسخ البيانات.
  • تدعم محركات الجداول MergeTree أنواعًا مختلفة من الإحصاءات وطرائق أخذ العينات للمساعدة في query optimization.
على الرغم من تشابه الاسم، فإن المحرّك Merge يختلف عن محركات *MergeTree.

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

للاطلاع على وصف مفصل للمعلمات، راجع تعليمة CREATE TABLE

عبارات الاستعلام

ENGINE

ENGINE — اسم المحرّك ومعلماته. ENGINE = MergeTree(). لا يحتوي محرّك MergeTree على أي معلمات.

ORDER BY

ORDER BY — مفتاح الفرز. Tuple من أسماء الأعمدة أو تعبيرات عشوائية. مثال: ORDER BY (CounterID + 1, EventDate). إذا لم يكن هناك مفتاح أساسي مُعرّف (أي لم يتم تحديد PRIMARY KEY)، يستخدم ClickHouse مفتاح الفرز بوصفه المفتاح الأساسي. إذا لم تكن هناك حاجة إلى الفرز، يمكنك استخدام الصياغة ORDER BY tuple(). وبدلاً من ذلك، إذا كان الإعداد create_table_empty_primary_key_by_default مفعّلًا، فستُضاف ORDER BY () ضمنيًا إلى عبارات CREATE TABLE. راجع Selecting a Primary Key.

PARTITION BY

PARTITION BYمفتاح التقسيم. وهو اختياري. في معظم الحالات، لا تحتاج إلى مفتاح تقسيم، وحتى إذا احتجت إلى التقسيم، فغالبًا لا تحتاج إلى مفتاح تقسيم أكثر دقة من التقسيم حسب الشهر. لا يسرّع التقسيم الاستعلامات (على عكس تعبير ORDER BY). يجب ألّا تستخدم مطلقًا تقسيمًا مفرط الدقة. لا تقسّم بياناتك حسب معرّفات العملاء أو الأسماء (وعوضًا عن ذلك، اجعل معرّف العميل أو الاسم هو العمود الأول في تعبير ORDER BY). للتقسيم حسب الشهر، استخدم التعبير toYYYYMM(date_column)، حيث إن date_column هو عمود يحتوي على تاريخ من النوع Date. وتكون أسماء الأقسام هنا بالتنسيق "YYYYMM".

PRIMARY KEY

PRIMARY KEY — المفتاح الأساسي إذا كان يختلف عن مفتاح الفرز. وهو اختياري. إن تحديد مفتاح فرز (باستخدام عبارة ORDER BY) يعني ضمنيًا تحديد مفتاح أساسي. وعادةً لا تكون هناك حاجة إلى تحديد المفتاح الأساسي إلى جانب مفتاح الفرز.

SAMPLE BY

SAMPLE BY — تعبير لأخذ العينات. اختياري. إذا تم تحديده، فيجب أن يكون ضمن المفتاح الأساسي. ويجب أن يُنتج تعبير أخذ العينات عددًا صحيحًا غير موقّع. مثال: SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID)).

TTL

TTL — قائمة بالقواعد التي تحدد مدة الاحتفاظ بالصفوف ومنطق نقل الأجزاء تلقائيًا بين الأقراص ووحدات التخزين. اختياري. يجب أن يُرجِع التعبير قيمة من النوع Date أو DateTime، مثل TTL date + INTERVAL 1 DAY. يحدد نوع القاعدة DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'|GROUP BY الإجراء الذي يجب تنفيذه على الجزء عند تحقق التعبير (أي عند بلوغه الوقت الحالي): حذف الصفوف منتهية الصلاحية، أو نقل جزء (إذا تحقق التعبير لجميع الصفوف في الجزء) إلى القرص المحدد (TO DISK 'xxx') أو إلى وحدة التخزين (TO VOLUME 'xxx')، أو تجميع القيم في الصفوف منتهية الصلاحية. النوع الافتراضي للقاعدة هو الحذف (DELETE). يمكن تحديد قائمة تضم عدة قواعد، لكن يجب ألا تكون هناك أكثر من قاعدة DELETE واحدة. لمزيد من التفاصيل، راجع TTL للأعمدة والجداول

الإعدادات

انظر إعدادات MergeTree. مثال على إعداد Sections
في المثال، نضبط التقسيم حسب الشهر. كما نضبط تعبيرًا لأخذ العينات على هيئة hash استنادًا إلى معرّف المستخدم. يتيح لك ذلك توزيع البيانات في الجدول توزيعًا شبه عشوائي لكل من CounterID وEventDate. وإذا حدّدت عبارة SAMPLE عند اختيار البيانات، فسيُرجع ClickHouse عيّنة بيانات شبه عشوائية ومتوازنة لمجموعة فرعية من المستخدمين. يمكن حذف إعداد index_granularity لأن 8192 هي القيمة الافتراضية.

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

يتكوّن الجدول من أجزاء بيانات مرتّبة حسب المفتاح الأساسي. عند إدراج البيانات في جدول، تُنشأ أجزاء بيانات منفصلة، ويُرتَّب كل جزء منها ترتيبًا معجميًا حسب المفتاح الأساسي. على سبيل المثال، إذا كان المفتاح الأساسي هو (CounterID, Date)، فإن البيانات داخل الجزء تُرتَّب حسب CounterID، وضمن كل CounterID تُرتَّب حسب Date. تُفصل البيانات التابعة لتقسيمات مختلفة في أجزاء مختلفة. وفي الخلفية، يدمج ClickHouse أجزاء البيانات لتحقيق تخزين أكثر كفاءة. ولا تُدمج الأجزاء التابعة لتقسيمات مختلفة. كما أن آلية الدمج لا تضمن وجود جميع الصفوف التي لها المفتاح الأساسي نفسه ضمن جزء البيانات نفسه. يمكن تخزين أجزاء البيانات بتنسيق Wide أو Compact. ففي تنسيق Wide يُخزَّن كل عمود في ملف منفصل ضمن نظام الملفات، أما في تنسيق Compact فتُخزَّن جميع الأعمدة في ملف واحد. ويمكن استخدام تنسيق Compact لتحسين أداء عمليات الإدراج الصغيرة والمتكررة. يتحكم في تنسيق تخزين البيانات الإعدادان min_bytes_for_wide_part وmin_rows_for_wide_part الخاصان بمحرك الجدول. فإذا كان عدد البايتات أو الصفوف في جزء البيانات أقل من قيمة الإعداد المقابل، يُخزَّن الجزء بتنسيق Compact. وإلا فيُخزَّن بتنسيق Wide. وإذا لم يُضبط أيّ من هذين الإعدادين، فتُخزَّن أجزاء البيانات بتنسيق Wide. يُقسَّم كل جزء بيانات منطقيًا إلى حبيبات. والحبيبة هي أصغر مجموعة بيانات غير قابلة للتجزئة يقرؤها ClickHouse عند تحديد البيانات. ولا يُجزِّئ ClickHouse الصفوف أو القيم، لذلك تحتوي كل حبيبة دائمًا على عدد صحيح من الصفوف. ويُعلَّم الصف الأول من الحبيبة بقيمة المفتاح الأساسي لذلك الصف. ولكل جزء بيانات، ينشئ ClickHouse ملف فهرس يخزّن العلامات. ولكل عمود، سواء أكان ضمن المفتاح الأساسي أم لا، يخزّن ClickHouse أيضًا العلامات نفسها. وتتيح لك هذه العلامات العثور على البيانات مباشرةً داخل ملفات الأعمدة. يُقيَّد حجم الحبيبة بالإعدادين index_granularity وindex_granularity_bytes الخاصين بمحرك الجدول. ويقع عدد الصفوف في الحبيبة ضمن النطاق [1, index_granularity]، وذلك بحسب حجم الصفوف. وقد يتجاوز حجم الحبيبة index_granularity_bytes إذا كان حجم صف واحد أكبر من قيمة الإعداد. وفي هذه الحالة، يكون حجم الحبيبة مساويًا لحجم الصف.

المفاتيح الأساسية والفهارس في الاستعلامات

خذ المفتاح الأساسي (CounterID, Date) على سبيل المثال. في هذه الحالة، يمكن توضيح الفرز والفهرس كما يلي:
إذا كان استعلام البيانات يحدد ما يلي:
  • CounterID in ('a', 'h')، يقرأ الخادم البيانات ضمن نطاقات العلامات [0, 3) و[6, 8).
  • CounterID IN ('a', 'h') AND Date = 3، يقرأ الخادم البيانات ضمن نطاقات العلامات [1, 3) و[7, 8).
  • Date = 3، يقرأ الخادم البيانات ضمن نطاق العلامات [1, 10].
تُظهر الأمثلة أعلاه أن استخدام فهرس يكون دائمًا أكثر فعالية من إجراء فحص كامل. يسمح الفهرس المتناثر بقراءة بيانات إضافية. عند قراءة نطاق واحد من المفتاح الأساسي، يمكن قراءة ما يصل إلى index_granularity * 2 من الصفوف الإضافية في كل كتلة بيانات. تتيح لك الفهارس المتناثرة العمل مع عدد كبير جدًا من صفوف الجدول، لأن مثل هذه الفهارس تتسع في معظم الحالات داخل RAM الخاصة بالحاسوب. لا يتطلب ClickHouse مفتاحًا أساسيًا فريدًا. يمكنك إدراج عدة صفوف بالمفتاح الأساسي نفسه. يمكنك استخدام تعبيرات من النوع Nullable في عبارتي PRIMARY KEY وORDER BY، ولكن لا يُنصح بذلك بشدة. للسماح بهذه الميزة، فعِّل الإعداد allow_nullable_key. وينطبق مبدأ NULLS_LAST على قيم NULL في عبارة ORDER BY.

اختيار مفتاح أساسي

عدد الأعمدة في المفتاح الأساسي غير محدود صراحةً. وبحسب بنية البيانات، يمكنك تضمين عدد أكبر أو أقل من الأعمدة في المفتاح الأساسي. وقد يترتب على ذلك ما يلي:
  • تحسين أداء الفهرس. إذا كان المفتاح الأساسي هو (a, b)، فإن إضافة عمود آخر c ستحسّن الأداء إذا تحققت الشروط التالية:
    • وجود استعلامات تتضمن شرطًا على العمود c.
    • شيوع نطاقات بيانات طويلة (أطول عدة مرات من index_granularity) ذات القيم المتطابقة لـ (a, b). وبعبارة أخرى، عندما تتيح لك إضافة عمود آخر تخطي نطاقات بيانات طويلة نسبيًا.
  • تحسين ضغط البيانات. يرتّب ClickHouse البيانات حسب المفتاح الأساسي، لذا كلما زاد التجانس كان الضغط أفضل.
  • توفير منطق إضافي عند دمج أجزاء البيانات في المحرّكين CollapsingMergeTree وSummingMergeTree. في هذه الحالة، من المنطقي تحديد مفتاح فرز يختلف عن المفتاح الأساسي.
يؤثر المفتاح الأساسي الطويل سلبًا في أداء الإدراج واستهلاك الذاكرة، لكن الأعمدة الإضافية في المفتاح الأساسي لا تؤثر في أداء ClickHouse أثناء استعلامات SELECT. يمكنك إنشاء جدول من دون مفتاح أساسي باستخدام الصياغة ORDER BY tuple(). في هذه الحالة، يخزّن ClickHouse البيانات بترتيب إدراجها. وإذا كنت تريد الحفاظ على ترتيب البيانات عند إدراجها بواسطة استعلامات INSERT ... SELECT، فاضبط max_insert_threads = 1. لاختيار البيانات بالترتيب الأصلي، استخدم استعلامات SELECT أحادية الخيط.

اختيار مفتاح أساسي يختلف عن مفتاح الفرز

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

استخدام الفهارس والتقسيمات في الاستعلامات

بالنسبة إلى استعلامات SELECT، يحلّل ClickHouse ما إذا كان يمكن استخدام فهرس. ويمكن استخدام الفهرس إذا كانت عبارة WHERE/PREWHERE تتضمن تعبيرًا (إما كأحد عناصر الاقتران أو بالكامل) يمثّل عملية مقارنة مساواة أو عدم مساواة، أو إذا كانت تتضمن IN أو LIKE مع بادئة ثابتة على أعمدة أو تعبيرات موجودة في المفتاح الأساسي أو مفتاح التقسيم، أو على بعض الدوال الجزئية التكرار لهذه الأعمدة، أو على العلاقات المنطقية بين هذه التعبيرات. وبذلك، يصبح من الممكن تنفيذ الاستعلامات بسرعة على نطاق واحد أو عدة نطاقات من المفتاح الأساسي. في هذا المثال، ستكون الاستعلامات سريعة عند تنفيذها لوسم تتبّع محدد، أو لوسم محدد مع نطاق زمني محدد، أو لوسم محدد مع تاريخ محدد، أو لعدة وسوم مع نطاق زمني، وهكذا. لنلقِ نظرة على الـ محرّك المُعَدّ كما يلي:
في هذه الحالة، ضمن الاستعلامات:
سيستخدم ClickHouse فهرس المفتاح الأساسي لاستبعاد البيانات غير ذات الصلة، ومفتاح التقسيم الشهري لاستبعاد الأقسام الواقعة ضمن نطاقات تاريخ غير ذات صلة. تُظهر الاستعلامات أعلاه أن الفهرس يُستخدم حتى مع التعبيرات المعقدة. وتُنظَّم القراءة من الجدول بحيث لا يمكن أن يكون استخدام الفهرس أبطأ من إجراء فحص كامل. في المثال أدناه، لا يمكن استخدام الفهرس.
للتحقق مما إذا كان ClickHouse يستطيع استخدام الفهرس عند تنفيذ استعلام، استخدم الإعدادين force_index_by_date وforce_primary_key. يتيح مفتاح التقسيم بحسب الشهر قراءة كتل البيانات التي تحتوي فقط على تواريخ ضمن النطاق المناسب. في هذه الحالة، قد تحتوي كتلة البيانات على بيانات تخص عدة تواريخ (حتى شهر كامل). وداخل الكتلة، تكون البيانات مرتبة حسب المفتاح الأساسي، الذي قد لا يتضمن التاريخ بوصفه العمود الأول. لذلك، فإن استخدام استعلام يتضمن شرطًا على التاريخ فقط ولا يحدد بادئة المفتاح الأساسي سيؤدي إلى قراءة بيانات أكثر مما يلزم عند الاستعلام عن تاريخ واحد.

استخدام الفهرس مع التعبيرات الحتمية في المفاتيح الأساسية

يمكن أن يحتوي المفتاح الأساسي على تعبيرات، لا على أسماء الأعمدة فقط. ولا تقتصر هذه التعبيرات على سلاسل دوال بسيطة، بل يمكن أن تكون أشجار تعبيرات كيفما كانت (مثل الدوال المتداخلة والتعبيرات المركبة)، ما دامت حتمية. يُعدّ التعبير حتميًا إذا كان يعيد دائمًا النتيجة نفسها لقيم الإدخال نفسها (على سبيل المثال: length(), toDate(), lower(), left(), cityHash64(), toUUID()؛ بخلاف now() أو rand()). وإذا كان المفتاح الأساسي يحتوي على تعبيرات حتمية، يمكن لـ ClickHouse تطبيقها على القيم الثابتة في الاستعلام واستخدام النتيجة لبناء شروط على فهرس المفتاح الأساسي. وهذا يتيح تخطّي البيانات لعبارات الشرط مثل =, IN, و has. ومن حالات الاستخدام الشائعة إبقاء المفتاح الأساسي مضغوطًا (مثل تخزين قيمة hash بدلًا من String طويل)، مع الاستمرار في إتاحة استخدام الفهرس لعبارات الشرط على العمود الأصلي. مثال على مفتاح أساسي حتمي (لكن غير حقني):
أمثلة على شروط التصفية التي يمكن أن تستخدم الفهرس:
في هذه الحالات، يحسب ClickHouse قيمة length('alice') (وسائر الثوابت) مرةً واحدة، ثم يستخدم قيم الأطوال لتضييق النطاقات في فهرس المفتاح الأساسي. وبما أن طول السلسلة ليس حقنيًا، فقد تتشارك سلاسل user_id المختلفة الطول نفسه، لذلك قد يقرأ الفهرس حبيبات إضافية (مطابقات إيجابية كاذبة). ومع ذلك، تظل النتيجة صحيحة لأن الشرط الأصلي (user_id = ... وIN وما إلى ذلك) يظل مطبَّقًا بعد القراءة. إذا كان التعبير الحتمي أيضًا حقنيًا (أي إن المدخلات المختلفة لا يمكن أن تنتج المخرج نفسه لأنواع argument المستخدمة)، فبوسع ClickHouse أيضًا استخدام الفهرس بفاعلية مع الصيغ المنفية: != وNOT IN وNOT has(...). على سبيل المثال، الدالتان reverse(p) وhex(p) حقنيتان بالنسبة إلى String. مثال على مفتاح أساسي حقني:
كما تُدعَم أيضًا التعبيرات الحقنية الأكثر تعقيدًا، على سبيل المثال:
أمثلة على الشروط التي يمكن أن تستخدم الفهرس:

استخدام الفهرس مع المفاتيح الأساسية الرتيبة جزئيًا

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

فهارس تخطي البيانات

يرد تعريف الفهرس في قسم الأعمدة ضمن استعلام CREATE.
بالنسبة إلى الجداول من عائلة *MergeTree، يمكن تحديد فهارس تخطي البيانات. تجمع هذه الفهارس بعض المعلومات عن التعبير المحدد على مستوى الكتل، التي تتكوّن من granularity_value من الحبيبات (ويُحدَّد حجم الحبيبة باستخدام الإعداد index_granularity في محرك الجدول). ثم تُستخدم هذه القيم المجمّعة في استعلامات SELECT لتقليل كمية البيانات المطلوب قراءتها من القرص، وذلك عبر تخطي كتل كبيرة من البيانات التي لا يمكن لعبارة where أن تستوفي الشرط فيها. يمكن حذف العبارة GRANULARITY، والقيمة الافتراضية لـ granularity_value هي 1. مثال
يمكن لـ ClickHouse استخدام الفهارس الواردة في المثال لتقليل حجم البيانات المقروءة من القرص في الاستعلامات التالية:
يمكن أيضًا إنشاء فهارس لتخطي البيانات على الأعمدة المركبة:

أنواع فهارس التخطي

يدعم محرك الجدول MergeTree الأنواع التالية من فهارس التخطي. لمزيد من المعلومات حول كيفية استخدام فهارس التخطي لتحسين الأداء، راجِع “فهم فهارس تخطي البيانات في ClickHouse”.

فهرس التخطي MinMax

لكل حبيبة فهرس، تُخزَّن القيمتان الصغرى والكبرى لتعبير ما. (إذا كان التعبير من النوع tuple، فإنه يخزّن القيمتين الصغرى والكبرى لكل عنصر من عناصر الـ tuple.)
Syntax

Set

يُخزَّن لكل كتلة فهرس ما يصل إلى max_rows من القيم الفريدة للتعبير المحدد. ويعني max_rows = 0 “تخزين جميع القيم الفريدة”.
Syntax

مرشح Bloom

يُخزَّن مرشح Bloom لكل حبيبة فهرس للأعمدة المحددة.
Syntax
يمكن أن تأخذ المعلمة false_positive_rate قيمة بين 0 و1 (القيمة الافتراضية: 0.025) وتحدّد احتمال توليد نتيجة إيجابية، مما يزيد كمية البيانات المقروءة. أنواع البيانات التالية مدعومة:
  • (U)Int*
  • Float*
  • Enum
  • Date
  • DateTime
  • String
  • FixedString
  • Array
  • LowCardinality
  • Nullable
  • UUID
  • Map
نوع البيانات Map: تحديد إنشاء الفهرس على المفاتيح أو القيمبالنسبة إلى نوع البيانات Map، يمكن للعميل تحديد ما إذا كان يجب إنشاء الفهرس على المفاتيح أو على القيم باستخدام الدالتين mapKeys أو mapValues.
نوع البيانات JSON: فهرسة مسارات JSONبالنسبة إلى نوع البيانات JSON، يمكن إنشاء فهرس bloom filter على مجموعة المسارات باستخدام الدالة JSONAllPaths. يتيح ذلك تخطي الحبيبات التي لا يحتوي فيها الاستعلام على مسار JSON موجود. راجع فهارس تخطي البيانات لـ JSON لمزيد من التفاصيل.

مرشح Bloom لـ N-gram (مهمل)

مع الوصول إلى مرحلة التوافر العام (GA) لفهرس text بدءًا من إصدار ClickHouse 26.2، لم يعد يُوصى باستخدام الفهرس ngrambf_v1 في البحث في النص الكامل.راجع صفحة “البحث في النص الكامل باستخدام فهارس النص” لمزيد من التفاصيل.
يُخزِّن كل حبيبة فهرس مرشح Bloom لـ n-grams الخاصة بالأعمدة المحددة.
Syntax
لا يعمل هذا الفهرس إلا مع أنواع البيانات التالية: لتقدير معلمات ngrambf_v1، يمكنك استخدام الدوال المعرّفة من قبل المستخدم (UDFs) التالية.
UDFs for ngrambf_v1
لاستخدام هذه الدوال، تحتاج إلى تحديد معلَمتين على الأقل:
  • total_number_of_all_grams
  • probability_of_false_positives
على سبيل المثال، إذا كان هناك 4300 ngrams في الـ حبيبة، وكنت تتوقع أن تكون الإيجابيات الكاذبة أقل من 0.0001، فيمكن بعد ذلك تقدير المعلَمات الأخرى بتنفيذ الاستعلامات التالية:
وبالطبع، يمكنك أيضًا استخدام تلك الدوال لتقدير المعلمات الخاصة بشروط أخرى. وتستند الدوال المذكورة أعلاه إلى حاسبة bloom filter هنا.

مرشح bloom للرموز

مع الإتاحة العامة (GA) للفهرس text بدءًا من إصدار ClickHouse 26.2، لم يعد الفهرس tokenbf_v1 موصى به لعمليات البحث في النص الكامل.راجع صفحة “البحث في النص الكامل باستخدام فهارس النص” لمزيد من التفاصيل.
Syntax

مرشح Bloom لـ sparse grams

مرشح Bloom لـ sparse grams مشابه لـ ngrambf_v1، لكنه يستخدم رموز sparse grams بدلًا من ngrams.
Syntax

فهرس نصي

ينشئ فهرسًا معكوسًا على بيانات نصية مُجزّأة إلى رموز، مما يتيح بحثًا نصيًا كاملًا بكفاءة وبشكل حتمي. راجع هنا لمزيد من التفاصيل.

تشابه المتجهات

يدعم البحث التقريبي عن أقرب الجيران، راجع هنا للاطلاع على التفاصيل.

دعم الدوال

تتضمن الشروط في عبارة WHERE استدعاءات لدوال تتعامل مع الأعمدة. وإذا كان العمود جزءًا من فهرس، يحاول ClickHouse استخدام هذا الفهرس عند تطبيق هذه الدوال. يدعم ClickHouse مجموعات فرعية مختلفة من الدوال التي يمكن معها استخدام الفهارس. يمكن استخدام الفهارس من النوع set مع جميع الدوال. أما أنواع الفهارس الأخرى، فهي مدعومة على النحو التالي: لا يمكن لـ ngrambf_v1 استخدام الدوال التي تحتوي على وسيطة ثابتة أصغر من حجم ngram لتحسين الاستعلامات. (*) لكي تكون hasTokenCaseInsensitive وhasTokenCaseInsensitiveOrNull فعّالتين، يجب إنشاء الفهرس tokenbf_v1 على بيانات محوّلة إلى أحرف صغيرة، على سبيل المثال INDEX idx (lower(str_col)) TYPE tokenbf_v1(512, 3, 0).
قد تُنتج مرشحات Bloom تطابقات إيجابية كاذبة، لذلك لا يمكن استخدام الفهارس ngrambf_v1 وtokenbf_v1 وsparse_grams وbloom_filter لتحسين الاستعلامات التي يُتوقع أن تكون نتيجة الدالة فيها false.على سبيل المثال:
  • يمكن تحسينه:
    • s LIKE '%test%'
    • NOT s NOT LIKE '%test%'
    • s = 1
    • NOT s != 1
    • startsWith(s, 'test')
  • لا يمكن تحسينه:
    • NOT s LIKE '%test%'
    • s NOT LIKE '%test%'
    • NOT s = 1
    • s != 1
    • NOT startsWith(s, 'test')

الإسقاطات

تشبه الإسقاطات العروض المُجسَّدة، لكنها تُعرَّف على مستوى الجزء. وهي توفّر ضمانات للاتساق إلى جانب استخدامها تلقائيًا في الاستعلامات.
عند تنفيذ الإسقاطات، ينبغي أيضًا مراعاة الإعداد force_optimize_projection.
لا تُدعَم الإسقاطات في عبارات SELECT التي تستخدم المُعدِّل FINAL.

استعلام الإسقاط

استعلام الإسقاط هو ما يعرّف الإسقاط. وهو يختار البيانات ضمنيًا من الجدول الأصلي. البنية
يمكن تعديل الإسقاطات أو حذفها باستخدام تعليمة ALTER.

فهارس الإسقاط

تُوسّع فهارس الإسقاط النظام الفرعي للإسقاط عبر توفير طريقة خفيفة وواضحة لتعريف فهارس على مستوى الإسقاط. ومن الناحية الظاهرية، يظل فهرس الإسقاط إسقاطًا، ولكن بصياغة أبسط وغرض أوضح: إذ يعرّف تعبيرًا مخصصًا للتصفية، بدلًا من تقديم بيانات مُحتسَبة ومخزَّنة مسبقًا. أما داخليًا، فلا يقوم فهرس الإسقاط بتمثيل الجدول الأصلي فعليًا بترتيب صفوف مُبدَّل كما يفعل الإسقاط العادي. وبدلًا من ذلك، يُخزَّن هذا التبديل في صورة عمود رقمي للتبديل _part_offset، أي SELECT _part_offset ORDER BY <index_expr>.

الصياغة

مثال:

أنواع الفهارس

الأنواع المدعومة حاليًا:
  • basic: يعادل فهرس MergeTree عاديًا على التعبير.
يوفّر هذا الإطار إمكانية إضافة المزيد من أنواع الفهارس مستقبلًا.

تخزين الإسقاطات

تُخزَّن الإسقاطات داخل دليل الجزء. وهي تشبه الفهرس، لكنها تتضمن دليلًا فرعيًا يخزّن جزءًا من جدول MergeTree مجهول الاسم. ويُشتق هذا الجدول من استعلام تعريف الإسقاط. إذا وُجد بند GROUP BY، يصبح محرك التخزين الأساسي هو AggregatingMergeTree، وتُحوَّل جميع الدوال التجميعية إلى AggregateFunction. وإذا وُجد بند ORDER BY، يستخدمه جدول MergeTree كتعبير المفتاح الأساسي. وأثناء عملية الدمج، يُدمَج جزء الإسقاط عبر إجراء الدمج الخاص بالتخزين التابع له. ويُدمَج المجموع الاختباري لجزء الجدول الأصل مع جزء الإسقاط. أما مهام الصيانة الأخرى فهي مشابهة لفهارس التخطي.

تحليل الاستعلام

  1. تحقّق مما إذا كان يمكن استخدام الإسقاط للإجابة عن الاستعلام المعطى، أي إنه ينتج النتيجة نفسها التي ينتجها الاستعلام على الجدول الأساسي.
  2. اختر أفضل تطابق ممكن، وهو التطابق الذي يتطلب قراءة أقل عدد من الحبيبات.
  3. سيكون مسار تنفيذ الاستعلام الذي يستخدم الإسقاطات مختلفًا عن ذلك الذي يستخدم الأجزاء الأصلية. وإذا كان الإسقاط غير موجود في بعض الأجزاء، فيمكننا إضافة مسار التنفيذ لإسقاطه أثناء التشغيل.

الوصول المتزامن إلى البيانات

للوصول المتزامن إلى الجدول، نستخدم آلية تعدد الإصدارات. وبعبارة أخرى، عند قراءة جدول وتحديثه في الوقت نفسه، تُقرأ البيانات من مجموعة الأجزاء الحالية وقت تنفيذ الاستعلام. ولا توجد أقفال طويلة الأمد. كما أن عمليات الإدراج لا تعيق عمليات القراءة. تُنفَّذ القراءة من الجدول بالتوازي تلقائيًا.

TTL للأعمدة والجداول

يحدّد مدة بقاء القيم. يمكن تعيين عبارة TTL للجدول بأكمله ولكل عمود على حدة. ويمكن أيضًا لـ TTL على مستوى الجدول تحديد آلية نقل البيانات تلقائيًا بين الأقراص ووحدات التخزين، أو إعادة ضغط الأجزاء التي انتهت صلاحية جميع البيانات فيها. يجب أن ينتج عن تقييم التعبيرات نوع البيانات Date أو Date32 أو DateTime أو DateTime64.
تجنّب الدوال غير الحتمية في تعبيرات TTLيُقيَّم TTL أثناء عمليات الدمج في الخلفية، وليس وقت الإدراج. تُعاد تقييم دوال مثل rand() وnow() وnow64() عند كل عملية دمج، مما يؤدي إلى سلوك حذف غير قابل للتنبؤ. يمنع ClickHouse التعبيرات التي لا تعتمد على أي عمود على الإطلاق، لكنه لا يرفض حاليًا الدوال غير الحتمية الممزوجة بإشارة إلى عمود (مثل ts + rand()). ينبغي أن تستند تعبيرات TTL فقط إلى قيم حتمية مشتقة من الأعمدة للحصول على نتائج قابلة للتنبؤ.
البنية تعيين مدة بقاء لعمود:
لتعريف interval، استخدم معاملات الفواصل الزمنية، على سبيل المثال:

TTL للعمود

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

إنشاء جدول باستخدام TTL:

إضافة TTL إلى عمود في جدول موجود

تعديل TTL للعمود

TTL للجدول

يمكن أن يحتوي الجدول على تعبير لإزالة الصفوف منتهية الصلاحية، وعلى عدة تعبيرات لنقل الأجزاء تلقائيًا بين الأقراص أو وحدات التخزين. وعندما تنتهي صلاحية الصفوف في الجدول، يحذف ClickHouse جميع الصفوف المقابلة. أما بالنسبة إلى نقل الأجزاء أو إعادة ضغطها، فيجب أن تستوفي جميع صفوف الجزء معايير تعبير TTL.
قد يأتي بعد كل تعبير TTL نوعٌ من قواعد TTL. ويحدد ذلك الإجراء الذي يجب تنفيذه عند تحقق التعبير (أي عند وصوله إلى الوقت الحالي):
  • DELETE - حذف الصفوف منتهية الصلاحية (الإجراء الافتراضي)؛
  • RECOMPRESS codec_name - إعادة ضغط جزء البيانات باستخدام codec_name؛
  • TO DISK 'aaa' - نقل الجزء إلى القرص aaa؛
  • TO VOLUME 'bbb' - نقل الجزء إلى القرص bbb؛
  • GROUP BY - تجميع الصفوف منتهية الصلاحية.
يمكن استخدام الإجراء DELETE مع عبارة WHERE لحذف بعض الصفوف منتهية الصلاحية فقط استنادًا إلى شرط تصفية:
يجب أن يكون تعبير GROUP BY بادئةً للمفتاح الأساسي للجدول. إذا لم يكن العمود جزءًا من تعبير GROUP BY ولم تُعيَّن له قيمة صراحةً في العبارة SET، فسيحتوي الصف الناتج على قيمة عشوائية من الصفوف المجمّعة (كما لو طُبِّقت عليه الدالة التجميعية any). أمثلة

إنشاء جدول مع TTL:

تعديل TTL للجدول:

إنشاء جدول، حيث تنتهي صلاحية الصفوف بعد شهر واحد. وتُحذف الصفوف منتهية الصلاحية التي توافق تواريخها يوم الاثنين:

إنشاء جدول يُعاد فيه ضغط الصفوف منتهية الصلاحية:

إنشاء جدول تُجمَّع فيه الصفوف منتهية الصلاحية. في صفوف النتيجة، تحتوي x على القيمة العظمى عبر الصفوف المجمَّعة، وy — على القيمة الصغرى، وd — على أي قيمة عشوائية من الصفوف المجمَّعة.

إزالة البيانات منتهية الصلاحية

تُزال البيانات التي انتهت صلاحية TTL الخاصة بها عندما يدمج ClickHouse أجزاء البيانات. عندما يكتشف ClickHouse أن البيانات منتهية الصلاحية، يُجري عملية دمج خارج الجدول الزمني. وللتحكم في وتيرة عمليات الدمج هذه، يمكنك تعيين merge_with_ttl_timeout. وإذا كانت القيمة منخفضة جدًا، فسينفّذ العديد من عمليات الدمج خارج الجدول الزمني، ما قد يستهلك قدرًا كبيرًا من الموارد. إذا نفّذت استعلام SELECT بين عمليات الدمج، فقد تحصل على بيانات منتهية الصلاحية. ولتجنب ذلك، استخدم استعلام OPTIMIZE قبل SELECT. انظر أيضًا

أنواع الأقراص

بالإضافة إلى أجهزة التخزين الكتلية المحلية، يدعم ClickHouse أنواع التخزين التالية:

استخدام عدة أجهزة تخزين كتلية لتخزين البيانات

المقدمة

يمكن لمحركات الجداول من عائلة MergeTree تخزين البيانات على عدة أجهزة تخزين كتلية. على سبيل المثال، قد يكون ذلك مفيدًا عندما تُقسَّم بيانات جدول معيّن ضمنيًا إلى بيانات “ساخنة” وأخرى “باردة”. تُطلَب أحدث البيانات بانتظام، لكنها لا تتطلب سوى مساحة صغيرة. في المقابل، نادرًا ما تُطلَب البيانات التاريخية الممتدة على مدى زمني طويل. وإذا توفرت عدة أقراص، فيمكن وضع البيانات “الساخنة” على أقراص سريعة (مثل أقراص NVMe SSD أو الذاكرة)، بينما تُحفَظ البيانات “الباردة” على أقراص أبطأ نسبيًا (مثل HDD). ينطبق ذلك على جميع أنواع الأقراص، بما في ذلك S3 وأقراص تخزين الكائنات الأخرى. على سبيل المثال، يمكنك توزيع البيانات عبر عدة حاويات S3 ضمن وحدة تخزين واحدة، أو إنشاء سياسات تخزين متدرجة تنقل البيانات من الأقراص المحلية إلى S3. راجع استخدام أقراص S3 مع عدة وحدات تخزين لمزيد من التفاصيل. يُعد جزء البيانات أصغر وحدة قابلة للنقل في الجداول التي تستخدم المحرك MergeTree. وتُخزَّن البيانات التابعة لجزء واحد على قرص واحد. ويمكن نقل أجزاء البيانات بين الأقراص في الخلفية (وفقًا لإعدادات المستخدم)، وكذلك باستخدام استعلامات ALTER.

المصطلحات

  • القرص — جهاز تخزين كتلي مُركَّب على نظام الملفات.
  • القرص الافتراضي — القرص الذي يخزّن المسار المحدد في إعداد الخادم path.
  • وحدة التخزين — مجموعة مرتبة من أقراص متماثلة (مشابهة لـ JBOD).
  • سياسة التخزين — مجموعة من وحدات التخزين والقواعد الخاصة بنقل البيانات بينها.
يمكن العثور على الأسماء المخصَّصة للكيانات الموصوفة في جداول النظام system.storage_policies وsystem.disks. لتطبيق إحدى سياسات التخزين المُعدّة على جدول، استخدم الإعداد storage_policy في جداول عائلة المحرك MergeTree.

التكوين

يجب تعريف الأقراص ووحدات التخزين وسياسات التخزين داخل الوسم <storage_configuration>، سواء في ملف ضمن الدليل config.d.
يمكن أيضًا تعريف الأقراص في قسم SETTINGS ضمن استعلام. ويكون ذلك مفيدًا في التحليلات المخصصة لإرفاق قرص مؤقتًا يكون مستضافًا، على سبيل المثال، على عنوان URL. راجع التخزين الديناميكي لمزيد من التفاصيل.
بنية التكوين:
الوسوم:
  • <disk_name_N> — اسم القرص. يجب أن تختلف الأسماء بين جميع الأقراص.
  • path — المسار الذي سيخزّن الخادم البيانات تحته (مجلدا data وshadow)، ويجب أن ينتهي بـ ’/’.
  • keep_free_space_bytes — مقدار مساحة القرص الحرة التي يجب الاحتفاظ بها.
ترتيب تعريف القرص غير مهم. بنية إعداد سياسات التخزين:
الوسوم:
  • policy_name_N — اسم السياسة. يجب أن تكون أسماء السياسات فريدة.
  • volume_name_N — اسم وحدة التخزين. يجب أن تكون أسماء وحدات التخزين فريدة.
  • disk — قرص ضمن وحدة تخزين.
  • max_data_part_size_bytes — الحد الأقصى لحجم الجزء الذي يمكن تخزينه على أيٍّ من أقراص وحدة التخزين. إذا كان الحجم التقديري لجزء مدموج أكبر من max_data_part_size_bytes، فسيُكتب هذا الجزء في وحدة التخزين التالية. تتيح هذه الميزة أساسًا الاحتفاظ بالأجزاء الجديدة/الصغيرة على وحدة تخزين سريعة (SSD) ثم نقلها إلى وحدة تخزين بطيئة (HDD) عندما يصبح حجمها كبيرًا. لا تستخدم هذا الإعداد إذا كانت سياستك تحتوي على وحدة تخزين واحدة فقط.
  • move_factor — عندما تنخفض المساحة المتاحة عن هذه النسبة، تبدأ البيانات تلقائيًا بالانتقال إلى وحدة التخزين التالية إن وُجدت (القيمة الافتراضية 0.1). يرتّب ClickHouse الأجزاء الموجودة حسب الحجم من الأكبر إلى الأصغر (ترتيبًا تنازليًا)، ثم يحدّد الأجزاء التي يكفي حجمها الإجمالي لتحقيق شرط move_factor. وإذا لم يكن الحجم الإجمالي لجميع الأجزاء كافيًا، فستنقل جميع الأجزاء.
  • perform_ttl_move_on_insert — يعطّل TTL move عند تنفيذ INSERT لجزء بيانات. افتراضيًا (إذا كان مُمكّنًا)، إذا أدرجنا جزء بيانات انتهت صلاحيته بالفعل وفق قاعدة TTL move، فإنه ينتقل فورًا إلى وحدة التخزين/القرص المعرَّف في قاعدة النقل. وقد يؤدي ذلك إلى إبطاء عملية INSERT بشكل كبير إذا كانت وحدة التخزين/القرص الوجهة بطيئة (مثل S3). وإذا كان معطّلًا، فسيُكتب جزء البيانات المنتهي الصلاحية أولًا في وحدة التخزين الافتراضية، ثم يُنقل مباشرة بعد ذلك إلى وحدة تخزين TTL.
  • load_balancing - سياسة موازنة الأقراص، round_robin أو least_used.
  • least_used_ttl_ms - يضبط مهلة التحديث (بالملي ثانية) للمساحة المتاحة على جميع الأقراص (0 - التحديث دائمًا، -1 - عدم التحديث مطلقًا، والقيمة الافتراضية هي 60000). لاحظ أنه إذا كان القرص مستخدمًا من ClickHouse فقط وغير خاضع لتوسيع/تقليص نظام الملفات أثناء التشغيل، فيمكنك استخدام -1. أما في جميع الحالات الأخرى فلا يُنصح بذلك، لأنه سيؤدي في النهاية إلى توزيع غير صحيح للمساحة.
  • prefer_not_to_merge — يجب ألا تستخدم هذا الإعداد. فهو يعطّل دمج أجزاء البيانات على وحدة التخزين هذه (وهذا ضار ويؤدي إلى تراجع الأداء). عند تمكين هذا الإعداد (لا تفعل ذلك)، لا يعود دمج البيانات على وحدة التخزين هذه مسموحًا به (وهذا أمر سيئ). يتيح هذا الإعداد (لكنك لا تحتاج إليه) التحكّم (وإذا كنت تريد التحكّم في شيء هنا، فأنت ترتكب خطأً) في كيفية تعامل ClickHouse مع الأقراص البطيئة (لكن ClickHouse يعرف الأفضل، لذا يُرجى عدم استخدام هذا الإعداد).
  • volume_priority — يحدّد الأولوية (الترتيب) الذي تُملأ به وحدات التخزين. القيمة الأقل تعني أولوية أعلى. يجب أن تكون قيم المعلَمة أعدادًا طبيعية، وأن تغطي مجتمعةً النطاق من 1 إلى N (بحيث تُمنح أقل أولوية) من دون تخطي أي أرقام.
    • إذا كانت جميع وحدات التخزين موسومة، فستُرتَّب أولوياتها وفق الترتيب المحدد.
    • إذا كانت بعض وحدات التخزين فقط موسومة، فستكون الوحدات غير الموسومة ذات الأولوية الأدنى، وتُرتَّب حسب ترتيب تعريفها في config.
    • إذا لم تكن أي من وحدات التخزين موسومة، فتُحدَّد أولويتها وفق ترتيب التصريح بها في التهيئة.
    • لا يمكن أن تكون لوحدتي تخزين قيمة الأولوية نفسها.
أمثلة على التهيئة:
في المثال المذكور، تنفّذ سياسة التخزين hdd_in_order أسلوب التوزيع بالتناوب. لذلك لا تحدد هذه السياسة سوى وحدة تخزين واحدة (single)، وتُخزَّن أجزاء البيانات على جميع أقراصها بالتتابع الدوري. يمكن أن تكون هذه السياسة مفيدة جدًا إذا كانت هناك عدة أقراص متشابهة مركّبة على النظام، ولكن RAID غير مهيأ. ضع في اعتبارك أن كل قرص على حدة غير موثوق، وقد تحتاج إلى تعويض ذلك بعامل replication مقداره 3 أو أكثر. إذا كانت هناك أنواع مختلفة من الأقراص متاحة في النظام، فيمكن استخدام سياسة moving_from_ssd_to_hdd بدلًا من ذلك. تتكوّن وحدة التخزين hot من قرص SSD (fast_ssd)، ويبلغ الحد الأقصى لحجم جزء يمكن تخزينه على هذه الوحدة 1GB. ستُخزَّن جميع الأجزاء التي يزيد حجمها على 1GB مباشرةً على وحدة التخزين cold، التي تحتوي على قرص HDD باسم disk1. كذلك، بمجرد أن تتجاوز نسبة امتلاء القرص fast_ssd 80%، ستُنقل البيانات إلى disk1 بواسطة عملية تعمل في الخلفية. يُعد ترتيب سرد وحدات التخزين داخل سياسة التخزين مهمًا إذا كانت واحدة على الأقل من وحدات التخزين المدرجة لا تحتوي على المعامل volume_priority بشكل صريح. وبمجرد امتلاء إحدى وحدات التخزين بشكل زائد، تُنقل البيانات إلى الوحدة التالية. ويُعد ترتيب سرد الأقراص مهمًا أيضًا لأن البيانات تُخزَّن عليها بالتناوب. عند إنشاء table، يمكن تطبيق إحدى سياسات التخزين المهيأة عليها:
تعني سياسة التخزين default استخدام وحدة تخزين واحدة فقط، تتكوّن من قرص واحد فقط محدَّد في <path>. يمكنك تغيير سياسة التخزين بعد إنشاء الجدول باستخدام استعلام [ALTER TABLE … MODIFY SETTING]، ويجب أن تتضمن السياسة الجديدة جميع الأقراص ووحدات التخزين القديمة نفسها وبالأسماء نفسها. يمكن تغيير عدد الخيوط التي تنفّذ عمليات نقل أجزاء البيانات في الخلفية عبر الإعداد background_move_pool_size.

التفاصيل

في حالة جداول MergeTree، تصل البيانات إلى القرص بطرق مختلفة:
  • نتيجةً لعملية insert (استعلام INSERT).
  • أثناء عمليات الدمج في الخلفية والطفرات.
  • عند التنزيل من نسخة متماثلة أخرى.
  • نتيجةً لتجميد القسم ALTER TABLE … FREEZE PARTITION.
في جميع هذه الحالات، باستثناء الطفرات وتجميد القسم، يُخزَّن الجزء على وحدة تخزين وقرص وفقًا لسياسة التخزين المحددة:
  1. تُختار أول وحدة تخزين (بحسب ترتيب تعريفها) تتوفر فيها مساحة قرص كافية لتخزين جزء (unreserved_space > current_part_size) وتسمح بتخزين أجزاء بهذا الحجم (max_data_part_size_bytes > current_part_size).
  2. ضمن وحدة التخزين هذه، يُختار القرص الذي يلي القرص المستخدم لتخزين الجزء السابق من البيانات، والذي تكون فيه المساحة الحرة أكبر من حجم الجزء (unreserved_space - keep_free_space_bytes > current_part_size).
داخليًا، تستخدم الطفرات وتجميد القسم الروابط الصلبة. لا تُدعَم الروابط الصلبة بين الأقراص المختلفة، لذلك في مثل هذه الحالات تُخزَّن الأجزاء الناتجة على الأقراص نفسها التي توجد عليها الأجزاء الأصلية. في الخلفية، تُنقَل الأجزاء بين وحدات التخزين استنادًا إلى مقدار المساحة الحرة (المعامل move_factor) وفقًا لترتيب تعريف وحدات التخزين في ملف الإعدادات. ولا تُنقَل البيانات مطلقًا من الأخيرة إلى الأولى. يمكن استخدام جداول النظام system.part_log (الحقل type = MOVE_PART) وsystem.parts (الحقلان path وdisk) لمراقبة عمليات النقل في الخلفية. كما يمكن العثور على معلومات تفصيلية في سجلات الخادم. يمكن للمستخدم فرض نقل جزء أو قسم من وحدة تخزين إلى أخرى باستخدام الاستعلام ALTER TABLE … MOVE PART|PARTITION … TO VOLUME|DISK …، مع مراعاة جميع القيود الخاصة بالعمليات التي تعمل في الخلفية. يبدأ الاستعلام عملية النقل بنفسه ولا ينتظر اكتمال العمليات في الخلفية. سيتلقى المستخدم رسالة خطأ إذا لم تتوفر مساحة حرة كافية أو إذا لم تتحقق أي من الشروط المطلوبة. لا يتداخل نقل البيانات مع تكرار البيانات. لذلك، يمكن تحديد سياسات تخزين مختلفة للجدول نفسه على نسخ متماثلة مختلفة. بعد اكتمال عمليات الدمج في الخلفية والطفرات، لا تُزال الأجزاء القديمة إلا بعد مرور مدة زمنية معينة (old_parts_lifetime). وخلال هذه المدة، لا تُنقَل إلى وحدات تخزين أو أقراص أخرى. لذلك، وحتى تُزال الأجزاء نهائيًا، فإنها تظل محسوبة عند تقييم مساحة القرص المشغولة. يمكن للمستخدم إسناد الأجزاء الكبيرة الجديدة إلى أقراص مختلفة ضمن وحدة تخزين من نوع JBOD بطريقة متوازنة باستخدام الإعداد min_bytes_to_rebalance_partition_over_jbod.

استخدام التخزين الخارجي لتخزين البيانات

يمكن لمحركات الجداول من عائلة MergeTree تخزين البيانات في S3 وAzureBlobStorage وHDFS باستخدام قرص من النوع s3 أو azure_blob_storage أو hdfs على الترتيب. راجع إعداد خيارات التخزين الخارجي لمزيد من التفاصيل. مثال على استخدام S3 كتخزين خارجي باستخدام قرص من النوع s3. صيغة التكوين:
راجع أيضًا تهيئة خيارات التخزين الخارجي.

استخدام أقراص S3 مع وحدات تخزين متعددة

يمكن استخدام أقراص S3 (وغيرها من أقراص التخزين الكائني) في سياسات التخزين متعددة الأقراص ومتعددة الوحدات بالطريقة نفسها المستخدمة مع الأقراص المحلية. يتيح لك ذلك توزيع البيانات عبر عدة حاويات S3 ضمن وحدة تخزين واحدة (على نمط JBOD)، أو إعداد سياسات تخزين متعددة الطبقات باستخدام وحدات S3. على سبيل المثال، لتوزيع البيانات عبر حاويتَي S3 بأسلوب round-robin:
يمكنك أيضًا الجمع بين وحدات التخزين المحلية ووحدات تخزين S3 ضمن سياسة تخزين متدرجة، على سبيل المثال نقل البيانات من قرص SSD محلي إلى S3 كلما تقادمت:
عند استخدام use_environment_credentials لمصادقة S3، تتم مشاركة بيانات الاعتماد البيئية (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) بين جميع أقراص S3. ولا يمكن استخدام بيانات اعتماد بيئية مختلفة لأقراص مختلفة. إذا كنت بحاجة إلى بيانات اعتماد مختلفة لكل قرص S3، فاستخدم بدلًا من ذلك إعدادَي access_key_id وsecret_access_key الصريحين لكل قرص.
يمكن إعداد جداول MergeTree غير المكررة وفق سيناريو كاتب واحد وعدة قرّاء على وحدة تخزين مشتركة. ويتحقق ذلك عبر التحديث التلقائي لقائمة الأجزاء، الذي يمكن إعداده على القرّاء. لاحظ أن ذلك يتطلب بيانات تعريف نظام ملفات مشتركة عبر النسخ المتماثلة (أو table_disk = true مع قرص محلي على مستوى الجدول). راجع refresh_parts_interval and table_disk.
إعدادات cacheتستخدم إصدارات ClickHouse من 22.3 إلى 22.7 إعدادات cache مختلفة. راجع using local cache إذا كنت تستخدم أحد هذه الإصدارات.

الأعمدة الافتراضية

  • _part — اسم الجزء.
  • _part_index — الفهرس التسلسلي للجزء في نتيجة الاستعلام.
  • _part_starting_offset — صف البداية التراكمي للجزء في نتيجة الاستعلام.
  • _part_offset — رقم الصف في الجزء.
  • _part_granule_offset — رقم الـ granule في الجزء.
  • _partition_id — اسم القسم.
  • _part_uuid — المعرّف الفريد للجزء (إذا كان إعداد MergeTree ‏assign_part_uuids مفعّلًا).
  • _part_data_version — إصدار بيانات الجزء (إما الحد الأدنى لرقم block أو إصدار mutation).
  • _partition_value — القيم (Tuple) الخاصة بتعبير partition by.
  • _sample_factor — عامل العينة (من الاستعلام).
  • _block_number — الرقم الأصلي للـ block الخاص بالصف الذي أُسنِد عند insert، ويُحفَظ عبر merges عندما يكون الإعداد enable_block_number_column مفعّلًا.
  • _block_offset — الرقم الأصلي للصف داخل block الذي أُسنِد عند insert، ويُحفَظ عبر merges عندما يكون الإعداد enable_block_offset_column مفعّلًا.
  • _disk_name — اسم الـ قرص المستخدم للتخزين.

إحصاءات الأعمدة

يرد تعريف الإحصاءات في قسم الأعمدة ضمن استعلام CREATE للجداول التابعة لعائلة *MergeTree*:
يمكننا أيضًا تعديل الإحصاءات باستخدام تعليمات ALTER:
توفّر هذه الإحصاءات خفيفة الوزن معلومات مجمّعة عن توزيع القيم في الأعمدة. وتُخزَّن الإحصاءات في كل جزء وتُحدَّث مع كل عملية إدراج. ولا يمكن استخدامها لتحسين PREWHERE إلا إذا فعّلنا set use_statistics = 1.

تقليم الأجزاء باستخدام الإحصاءات

عند تمكين use_statistics_for_part_pruning، يمكن استخدام الإحصاءات في تقليم الأجزاء. حاليًا، لا يدعم تقليم الأجزاء إلا إحصاءات basic (وإحصاءات minmax المهملة). وعند تعريف مثل هذه الإحصاءات لعمود، يتتبّع ClickHouse القيمتَين الصغرى والكبرى لذلك العمود في كل جزء. يتيح تقليم الأجزاء تخطي قراءة أجزاء البيانات كاملةً عندما يتعذر على شرط التصفية في الاستعلام مطابقة أي صفوف في ذلك الجزء. مثال:

الأنواع المتاحة من إحصاءات الأعمدة

  • basic حزمة مضغوطة من الملخصات ذات القيمة الواحدة المستمدة من عمود. وبحسب نوع العمود، تُعبَّأ العناصر التالية:
    • لأي عمود تُمثَّل قيمه كأعداد (أعداد صحيحة، أعداد فاصلة عائمة، Decimal*، Date*، DateTime*، Enum*، IPv4، …): القيمة الدنيا والقيمة القصوى، مما يتيح تقدير انتقائية مرشحات النطاق وتمكين تقليم الأجزاء؛
    • لأعمدة String وFixedString: إجمالي طول القيم غير NULL بالبايت (الذي يمكن اشتقاق متوسط طول السلسلة منه)؛
    • لأعمدة Nullable وLowCardinality(Nullable): عدد قيم NULL، الذي يستخدمه المُحسِّن لاستبعاد صفوف NULL من تقديرات الانتقائية. يمكن لإحصائية basic واحدة أن تعبّئ عدة عناصر من هذه في الوقت نفسه — فعلى سبيل المثال، في عمود Nullable(UInt32)، تتتبّع كلاً من الحد الأدنى/الأقصى الرقمي وعدد قيم NULL. وبالمقارنة مع minmax، تعمل basic أيضاً مع أعمدة String / FixedString، ويمكن تعريفها على أغلفة Nullable لأنواع مثل UUID أو IPv6 فقط لتتبّع عدد قيم NULL.
  • minmax (مهمل)
إحصاءات minmax مهملة ولم يعد بالإمكان إنشاؤها (CREATE TABLE ... STATISTICS(minmax) وALTER TABLE ... ADD/MODIFY STATISTICS ... TYPE minmax تُرجع خطأ). تظل الجداول والأجزاء الحالية التي تحتوي على إحصاءات minmax تعمل. استخدم إحصاءات basic بدلاً منها.
  • tdigest
إحصاءات النوع tdigest لها تكاليف إنشاء مرتفعة وقد تؤدي إلى إبطاء استيعاب البيانات.
هياكل TDigest التقريبية التي تتيح حساب النسب المئوية التقريبية (مثل المئين التسعين) للأعمدة الرقمية.
  • uniq هياكل BJKST التقريبية التي توفّر تقديراً لعدد القيم المميزة التي يحتويها العمود. وتستخدم داخلياً uniq.
  • uniq_v2 مشابه لـ uniq، لكنه يستخدم داخلياً uniqCombined(12) (وهو أحد أشكال HyperLogLog). يستهلك ذاكرة أقل من uniq ويمكن بناؤه بسرعة أكبر.
  • countmin
إحصاءات النوع countmin لها تكاليف إنشاء مرتفعة وقد تؤدي إلى إبطاء استيعاب البيانات.
هياكل CountMin التقريبية التي توفّر عدّاً تقريبياً لتكرار كل قيمة في العمود.

أنواع البيانات المدعومة

كل ما سبق يقبل أيضًا مغلّفات Nullable وLowCardinality(Nullable) للأنواع المدرجة. ويمكن أيضًا تعريف Basic على مغلّفات Nullable لأنواع مثل UUID أو IPv6 فقط لتتبّع عدد قيم NULL.

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

بالنسبة إلى basic على الأعمدة String / FixedString، لا يسجّل هذا الإحصاء إلا إجمالي طول البايتات للقيم غير NULL (ويُستخدم لتقدير متوسط طول السلسلة النصية) وعدد قيم NULL؛ ولا يُعتمد عليه في مرشحات النطاق أو تقليم الأجزاء.

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

يمكن تجاوز بعض إعدادات MergeTree على مستوى العمود:
  • max_compress_block_size — الحد الأقصى لحجم كتل البيانات غير المضغوطة قبل ضغطها لكتابتها في جدول.
  • min_compress_block_size — الحد الأدنى لحجم كتل البيانات غير المضغوطة المطلوب لضغطها عند كتابة العلامة التالية.
مثال:
يمكن تعديل إعدادات مستوى العمود أو إزالتها باستخدام ALTER MODIFY COLUMN، على سبيل المثال:
  • إزالة SETTINGS من تعريف العمود:
  • عدّل إعدادًا:
  • إعادة تعيين إعداد واحد أو أكثر، مع إزالة تعريف الإعداد أيضًا من تعبير العمود في استعلام CREATE الخاص بالجدول.
آخر تعديل في ٢٤ يوليو ٢٠٢٦