- تكون تغييرات الجداول غير متكررة أو عندما تكون عمليات التحديث الدفعي مقبولة.
- لا تكون العلاقات من متعدد إلى متعدد، أو لا تكون ذات كاردينالية مرتفعة بشكل مفرط.
- لن يُستعلَم إلا عن مجموعة فرعية محدودة من الأعمدة، أي يمكن استبعاد أعمدة معيّنة من إزالة التطبيع.
- تكون لديك القدرة على نقل المعالجة خارج ClickHouse إلى أنظمة المصدر مثل Flink، حيث يمكن إدارة الإثراء الفوري أو التسطيح.
عندما تكون عمليات JOIN مطلوبة
- تجنّب النواتج الديكارتية: إذا كانت قيمة في الجانب الأيسر تطابق عدة قيم في الجانب الأيمن، فستُرجع JOIN عدة صفوف — وهو ما يُعرف بالناتج الديكارتي. وإذا كانت حالة الاستخدام لديك لا تتطلب كل التطابقات من الجانب الأيمن، بل أي تطابق واحد فقط، فيمكنك استخدام عمليات JOIN من النوع
ANY(مثلLEFT ANY JOIN). فهي أسرع وتستخدم ذاكرة أقل من عمليات JOIN العادية. - قلّل أحجام الجداول المنضمّة: يزداد زمن التنفيذ واستهلاك الذاكرة في عمليات JOIN تناسبيًا مع حجمَي الجدولين الأيسر والأيمن. ولتقليل كمية البيانات التي تعالجها JOIN، أضف شروط تصفية إضافية في العبارتين
WHEREأوJOIN ONداخل الاستعلام. يدفع ClickHouse شروط التصفية إلى أعمق نقطة ممكنة في query plan، وغالبًا قبل عمليات JOIN. وإذا لم تُدفَع عوامل التصفية إلى الأسفل تلقائيًا (لأي سبب)، فأعد كتابة أحد جانبي JOIN على هيئة استعلام فرعي لفرض هذا الـ pushdown. - استخدم direct joins عبر القواميس عند الاقتضاء: تُنفَّذ عمليات JOIN القياسية في ClickHouse على مرحلتين: مرحلة build، حيث يُجرى التكرار على الجانب الأيمن لبناء hash table، تليها مرحلة probe، حيث يُجرى التكرار على الجانب الأيسر للعثور على الأطراف المطابقة في JOIN عبر عمليات lookup في hash table. إذا كان الجانب الأيمن قاموسًا أو table engine آخر بخصائص key-value (مثل EmbeddedRocksDB أو محرك الجدول Join)، فيمكن لـ ClickHouse استخدام خوارزمية join “direct”، ما يلغي فعليًا الحاجة إلى بناء hash table ويُسرّع query processing. يعمل ذلك مع JOIN من النوع
INNERوLEFT OUTER، ويُفضَّل لأعباء العمل التحليلية real-time. - استفد من فرز الجداول في عمليات JOIN: يُرتَّب كل جدول في ClickHouse حسب أعمدة primary key الخاصة به. ويمكن الاستفادة من هذا الترتيب باستخدام ما يُعرف بخوارزميات sort-merge JOIN مثل
full_sorting_mergeوpartial_merge. وعلى عكس خوارزميات JOIN القياسية المعتمدة على hash tables (انظر أدناه:parallel_hashوhashوgrace_hash)، تقوم خوارزميات sort-merge JOIN أولًا بفرز الجدولين ثم دمجهما. وإذا كان الاستعلام يُجري JOIN على كلا الجدولين باستخدام أعمدة primary key الخاصة بكل منهما، فهناك تحسين في sort-merge يتخطى خطوة الفرز، مما يوفر وقت المعالجة ويقلل overhead. - تجنّب عمليات JOIN التي يحدث فيها spill إلى disk: قد تصبح الحالات الوسيطة في عمليات JOIN (مثل hash tables) كبيرة لدرجة أنها لا تعود تتسع في main memory. في هذه الحالة، سيُرجع ClickHouse افتراضيًا خطأ نفاد الذاكرة. بعض خوارزميات join (انظر أدناه)، مثل
grace_hashوpartial_mergeوfull_sorting_merge، قادرة على spill الحالات الوسيطة إلى disk ومواصلة تنفيذ الاستعلام. ومع ذلك، ينبغي استخدام خوارزميات join هذه بحذر، لأن الوصول إلى disk قد يبطئ query processing بشكل كبير. ونوصي بدلًا من ذلك بتحسين استعلام JOIN بطرق أخرى لتقليل حجم الحالات الوسيطة. - القيم الافتراضية كعلامات على عدم وجود تطابق في outer JOINs: تتضمن left/right/full outer joins جميع القيم من الجدول الأيسر أو الأيمن أو كليهما. وإذا لم يُعثر على طرف مطابق في JOIN داخل الجدول الآخر لبعض القيم، يستبدل ClickHouse هذا الطرف بعلامة خاصة. ويفرض معيار SQL أن تستخدم databases القيمة NULL بوصفها هذه العلامة. في ClickHouse، يتطلب هذا تغليف عمود النتيجة في Nullable، مما يضيف عبئًا إضافيًا على الذاكرة والأداء. وكبديل، يمكنك ضبط الإعداد
join_use_nulls = 0واستخدام default value لنوع بيانات عمود النتيجة بوصفها العلامة.
استخدم القواميس بحذرعند استخدام القواميس في عمليات JOIN في ClickHouse، من المهم فهم أنها، بحكم تصميمها، لا تسمح بالمفاتيح المكررة. أثناء تحميل البيانات، تُزال المفاتيح المكررة بصمت، ولا يُحتفَظ إلا بآخر قيمة جرى تحميلها لمفتاح معيّن. وهذا يجعل القواميس مثالية للعلاقات من واحد إلى واحد أو من متعدد إلى واحد، حيث تكون الحاجة إلى أحدث قيمة أو إلى القيمة المعتمدة فقط. لكن استخدام قاموس في علاقة من واحد إلى متعدد أو من متعدد إلى متعدد (مثل ربط الأدوار بالجهات الفاعلة، حيث يمكن أن تكون للجهة الفاعلة الواحدة عدة أدوار) سيؤدي إلى فقدان صامت للبيانات، لأن جميع الصفوف المتطابقة، باستثناء صف واحد، ستُستبعَد. لذلك، لا تُعد القواميس مناسبة للحالات التي تتطلب الحفاظ الكامل على العلاقات عند وجود عدة مطابقات. ولمعرفة المزيد عن الحالات التي تكون فيها القواميس مفيدة (ومتى لا تكون كذلك)، راجع أفضل ممارسات القواميس.
اختيار خوارزمية JOIN المناسبة
- Parallel Hash JOIN (default): سريع للجداول الموجودة على الجانب الأيمن ذات الحجم الصغير إلى المتوسط والتي تتسع في الذاكرة.
- Direct JOIN: مثالي عند استخدام القواميس (أو محركات الجداول الأخرى ذات خصائص key-value) مع
INNERأوLEFT ANY JOIN— وهو أسرع أسلوب لعمليات lookup المباشرة، لأنه يلغي الحاجة إلى إنشاء hash table. - Full Sorting Merge JOIN: فعّال عندما يكون كلا الجدولين مرتَّبين حسب join key.
- Partial Merge JOIN: يقلّل استهلاك الذاكرة إلى الحد الأدنى، لكنه أبطأ — وهو الأنسب لربط الجداول الكبيرة عند محدودية الذاكرة.
- Grace Hash JOIN: مرن وقابل لضبط الذاكرة، ومناسب لمجموعات البيانات الكبيرة مع إمكانية ضبط خصائص الأداء.
يختلف دعم كل خوارزمية لأنواع JOIN. ويمكن العثور على قائمة كاملة بأنواع JOIN المدعومة لكل خوارزمية هنا.
join_algorithm = 'auto' (وهو الإعداد الافتراضي)، أو التحكّم بها صراحةً وفقًا لـ workload لديك. وإذا كنت بحاجة إلى اختيار خوارزمية JOIN لتحسين الأداء أو تقليل overhead الذاكرة، فنوصي بهذا الدليل.
لتحقيق أفضل أداء:
- اجعل استخدام عمليات JOIN في الحد الأدنى ضمن workloads عالية الأداء.
- تجنّب استخدام أكثر من 3–4 joins في query واحدة.
- أجرِ benchmark لخوارزميات مختلفة على بيانات حقيقية — إذ يختلف الأداء بحسب توزيع join key وحجم البيانات.