join_algorithm
يحدد خوارزمية JOIN المستخدمة. يمكن تحديد عدة خوارزميات، وتُختار الخوارزمية المناسبة لكل استعلام بحسب kind/صرامة ومحرك الجدول. تُضبط الكتابة للخوارزميات القائمة على التجزئة بمعزل عنjoin_algorithm. تستخدم جميع الخوارزميات القائمة على التجزئة max_bytes_before_external_join وmax_bytes_ratio_before_external_join بوصفهما عتبتين للكتابة.
تؤثر معظم الخوارزميات في الاستعلام فقط عندما تكون هي الخوارزمية المحددة له. لكن بعضها يغيّر التخطيط بمجرد إدراجه، حتى لو كان خيارًا احتياطيًا منخفض الأولوية لا يُختار في النهاية، لأن القرار يُتخذ قبل اختيار الخوارزمية. ويوجد تأثيران من هذا النوع:
- يصبح استدلال النوع لـ مفتاح الربط أكثر صرامة (فعلى سبيل المثال، لا يمكن لـ merge join ضم مفاتيح من أنواع مختلفة مثل
StringوNullable(String)). قد يغيّر ذلك الأنواع الناتجة لأعمدةUSING، وقد يؤدي إلى فشل join مع جدول ذات engine من نوعJoinبسببTYPE_MISMATCH. يتم تفعيله بواسطةfull_sorting_mergeوparallel_full_sorting_merge. - يحصل
ORDER BY ... LIMITعلى الجانب المحفوظ من join على sort صريح بدلًا من قراءة بترتيب primary key، لأن join يُفترض أنه يقطع القراءة المرتبة (تُدرج merge join sort خاصًا بها قبل join؛ وتُعيد partial merge join فرز كتل اليسرى؛ كما أن join التي يمكنها إنتاج كتل مؤجلة لا تنشر القراءة المرتبة أيضًا). تكون النتيجة نفسها، لكن plan أقل كفاءة. يتم تفعيله بواسطةfull_sorting_mergeوparallel_full_sorting_mergeوpartial_mergeوprefer_partial_mergeوgrace_hashوauto، وكذلك بواسطة قيمة غير صفرية لـmax_bytes_before_external_join/max_bytes_ratio_before_external_join.
hash أو خوارزمية أخرى. إذا كان ذلك غير مرغوب فيه، فلا تدرج الخوارزميات المذكورة أعلاه في join_algorithm للاستعلامات المتأثرة.
القيم الممكنة:
- grace_hash
grace_hash صراحةً استخدامه أساسًا لأغراض التشخيص. لتمكين الكتابة لـ join، اضبط max_bytes_before_external_join أو max_bytes_ratio_before_external_join بدلًا من ذلك.
يكون grace_hash خارجيًا بدءًا من الكتلة الأولى: إذ يُقسَّم الجدول الأيمن فورًا، بينما يجمعه hash وparallel_hash في الذاكرة أولًا ولا يقسمانه إلا بعد تجاوز عتبة الكتابة. اختره عندما تعرف مسبقًا أن الجانب الأيمن لن يتسع في الذاكرة وتريد تجاوز المرحلة داخل الذاكرة. عتبة الكتابة نفسها هي التي تستخدمها كل خوارزمية hash، وهي max_bytes_before_external_join / max_bytes_ratio_before_external_join، ويجب أن تكون إحدى القيمتين غير صفرية ما لم يكن legacy_join_size_limits_trigger_spilling مفعّلًا. دون عتبة، يُتجاوز grace_hash إلى الخوارزمية التالية في القائمة، ويُرفض إذا كان الخوارزمية الوحيدة.
تقرأ المرحلة الأولى من grace join الجدول الأيمن وتقسّمه إلى N حاوية بحسب قيمة hash لأعمدة المفتاح (يكون N في البداية grace_hash_join_initial_buckets). ويتم ذلك بطريقة تضمن إمكانية معالجة كل حاوية بشكل مستقل. تُضاف صفوف من الحاوية الأولى إلى hash جدول في الذاكرة بينما تُحفظ البقية على القرص. وإذا تجاوز حجم hash جدول عتبة الكتابة، يزداد عدد الحاويات مع إعادة تعيين الحاوية المخصصة لكل صف. وأي صفوف لا تنتمي إلى الحاوية الحالية تُفرَّغ ويُعاد تعيينها.
يدعم INNER/LEFT/RIGHT/FULL ALL/ANY JOIN.
- hash
OR في قسم JOIN ON.
عند استخدام خوارزمية hash، يُرفَع الجزء الأيمن من JOIN إلى الذاكرة.
- parallel_hash
hash تقسّم البيانات إلى حاويات وتبني عدة جداول hash بدلًا من واحدة بشكل متزامن لتسريع هذه العملية.
عند استخدام خوارزمية parallel_hash، يُرفَع الجزء الأيمن من JOIN إلى الذاكرة.
- partial_merge
RIGHT JOIN وFULL JOIN إلا مع صرامة من نوع ALL (أما SEMI وANTI وANY وASOF فغير مدعومة).
عند استخدام خوارزمية partial_merge، يفرز ClickHouse البيانات ويكتبها على القرص. تختلف خوارزمية partial_merge في ClickHouse قليلًا عن التنفيذ التقليدي. أولًا، يفرز ClickHouse الجدول الأيمن بحسب مفاتيح الربط على شكل كتل وينشئ min-max index للـ كتل المرتبة. ثم يفرز أجزاءً من الجدول الأيسر بحسب join key ويجري join لها مع الجدول الأيمن. ويُستخدم min-max index أيضًا لتخطي كتل غير الضرورية من الجدول الأيمن.
- direct
direct (المعروفة أيضًا باسم nested loop) عملية lookup في الجدول الأيمن باستخدام صفوف من الجدول الأيسر كمفاتيح.
وهي مدعومة في أنواع تخزين خاصة مثل Dictionary وEmbeddedRocksDB وجداول من نوع MergeTree.
بالنسبة إلى MergeTree جداول، تدفع الخوارزمية مرشحات مفتاح الربط مباشرةً إلى storage layer. وقد يكون ذلك أكثر كفاءة عندما يتمكن المفتاح من استخدام primary key index الخاص بالجدول في عمليات lookup، وإلا فإنها تُجري فحصًا كاملًا للجدول الأيمن لكل كتلة من الجدول الأيسر.
يدعم INNER وLEFT joins، ومفاتيح الربط أحادية العمود للمساواة فقط، من دون شروط أخرى.
- auto
auto، تتم تجربة join hash أولًا، ثم يجري التبديل تلقائيًا أثناء التنفيذ إلى خوارزمية أخرى إذا تم تجاوز memory limit.
- full_sorting_merge
- ie_join
JOIN يحتوي قسم ON فيه على مقارنتين لعدم المساواة (< و<= و> و>=) بين تعبيرات الجداول الموصولة. تدعم ALL INNER/LEFT/RIGHT/FULL JOIN وSEMI/ANTI LEFT/RIGHT JOIN.
يحدد موضعها في القائمة الأولوية. فإذا أُدرجت بعد الخوارزميات الأخرى، كما في القيمة الافتراضية، فلا تُستخدم ie_join إلا عندما لا يحتوي قسم ON على شروط مساواة. وإذا أُدرجت أولًا، فتُستخدم كلما احتوى قسم ON على شرطي عدم مساواة، وتُطبَّق أي شروط متبقية كعامل تصفية على نتيجة الربط. عندما يوجد أكثر من شرطي عدم مساواة، يُختار الشرطان اللذان تستخدمهما الخوارزمية بحسب انتقائيتهما المقدرة من إحصاءات الأعمدة؛ وفي غياب الإحصاءات، يُستخدم أول شرطين بحسب ترتيب ورودهما في الصياغة. من دون ie_join في القائمة، يُنفَّذ INNER JOIN الذي لا يحتوي إلا على شروط عدم مساواة بوصفه CROSS JOIN مع عامل تصفية، وتُنفَّذ أنواع الربط الأخرى بوصفها ربطًا بحلقات متداخلة على مستوى الكتل.
يُجمَّع كلا الإدخالين في memory قبل joining: يحدّ كل من max_rows_in_join وmax_bytes_in_join الإدخال المتراكم لكلا الجانبين معًا (وليس right side فقط)، مع تحديد الإجراء عند overflow عبر join_overflow_mode؛ ولا تُحتسب ضمن الحد فهارس sort التي يبنيها المشغّل فوق الإدخال المتراكم. ويعمل مشغّل join نفسه في خيط تنفيذ واحد؛ ولا يجري التوازي إلا في عمليات sort السابقة لـ join على الإدخالات.
- parallel_full_sorting_merge
full_sorting_merge، لكن joins القائمة على equality والمتوافقة مع hash تُقسَّم إلى shards بحسب hash الخاص بمفاتيح الربط إلى merge joins مستقلة لكل shard تعمل بالتوازي (حتى max_threads)، بدلًا من merge join واحدة. وهذا يحافظ على memory usage المنخفض والمتدفق الخاص بـ merge join مع استخدام جميع خيوط التنفيذ، ولا تكون النتيجة مرتبة.
ولا يُطبَّق التقسيم إلى shards بحسب hash لمفاتيح الربط إلا على joins القائمة على equality البسيطة على أنواع مفاتيح يتسق hash الخاص بها مع مقارنة merge-join، وفقط عندما لا يكون أي من الجانبين مرتبًا مسبقًا. ويُتخطّى في الحالات التالية:
- joins من نوع
ASOF، وأنواع المفاتيح floating-point وJSONوObjectوDynamic: إذ لا تتسق hashes الخاصة بها مع مقارنة merge-join، ولذلك قد تصل المفاتيح المتساوية إلى shards مختلفة. - الجوانب المرتبة مسبقًا (قراءة MergeTree بالترتيب، أو أي إدخال مرتب مسبقًا): قد يؤدي scatter المحافظ على الترتيب إلى merge joins لكل shard إلى حدوث deadlock في pipeline. ويُحتفَظ بدلًا من ذلك بالقراءة بالترتيب وتحسينها
read_in_order_use_virtual_row. - أثناء قيام initiator ببناء distributed plan (
make_distributed_plan)، لأن scattered sort غير قابل للتسلسل من أجل التنفيذ عن بُعد. تُعيد local single-fragment plan وfragments الخاصة بكل worker التحسين مع تعطيل ذلك الإعداد، ولذلك لا يزال بإمكانها التقسيم إلى shards.
full_sorting_merge واحدة، ولا يزال بالإمكان تقسيم جوانب MergeTree المقروءة بالترتيب إلى shards عند المصدر بحسب ranges الخاصة بـ primary key (والتي تُرتَّب بالمقارنة نفسها التي يستخدمها join، ولذلك تبقى المفاتيح المتساوية معًا) عندما يكون query_plan_join_shard_by_pk_ranges مفعّلًا.
- prefer_partial_merge
partial_merge إن أمكن، وإلا فإنه يستخدم hash. تم إهمال، مماثل لـ partial_merge,hash.
- default (تم إهمال)
direct,hash، أي محاولة استخدام direct join ثم hash join (بهذا الترتيب).
join_any_take_last_row
يغيّر سلوك عمليات JOIN ذات الصرامةANY عندما يحتوي الجدول الأيمن على أكثر من صف مطابق واحد للمفتاح.
ينطبق هذا الإعداد على جداول محرك
Join وخوارزميات JOIN القائمة على التجزئة. ولا تأثير له على ربط بحلقات متداخلة على مستوى الكتل، إذ لا توجد في غياب مفتاح الربط مجموعة من الصفوف المطابقة يمكن أخذ آخرها.إذا تم إنشاء JOIN بالتوازي، فقد يصبح ترتيب الصفوف غير حتمي. وهذا يعني أن join_any_take_last_row = 1 قد يعيد صفًا غير حتمي في استعلامات ANY JOIN.- 0 — إذا كان الجدول الأيمن يحتوي على أكثر من صف مطابق واحد، فلن يُضم إلا أول صف يتم العثور عليه.
- 1 — إذا كان الجدول الأيمن يحتوي على أكثر من صف مطابق واحد، فلن يُضم إلا آخر صف يتم العثور عليه.
join_default_strictness
يضبط درجة الصرامة الافتراضية لعبارات JOIN. القيم الممكنة:ALL— إذا كان الجدول الأيمن يحتوي على عدة صفوف متطابقة، ينشئ ClickHouse حاصلًا كارتيسيًا من الصفوف المتطابقة. وهذا هو سلوكJOINالمعتاد في standard SQL.ANY— إذا كان الجدول الأيمن يحتوي على عدة صفوف متطابقة، فلا يُضم سوى أول صف يُعثر عليه. وإذا كان الجدول الأيمن يحتوي على صف متطابق واحد فقط، فستكون نتائجANYوALLمتطابقة.ASOF— لربط التسلسلات ذات المطابقة غير المؤكدة.Empty string— إذا لم يتم تحديدALLأوANYفي الاستعلام، يطرح ClickHouse استثناء.
join_on_disk_max_files_to_merge
يحدّ من عدد الملفات المسموح به للفرز المتوازي في عمليات MergeJoin عند تنفيذها على القرص. كلما زادت قيمة الإعداد، زاد استخدام RAM وقلّت الحاجة إلى عمليات الإدخال/الإخراج على القرص. القيم الممكنة:- أي عدد صحيح موجب يبدأ من 2.
join_output_by_rowlist_perkey_rows_threshold
الحد الأدنى لمتوسط عدد الصفوف لكل مفتاح في الجدول الأيمن، لتحديد ما إذا كان سيتم إخراج النتائج وفق قائمة الصفوف في hash join.join_overflow_mode
يحدّد هذا الإعداد الإجراء الذي ينفّذه ClickHouse عندما تصل عملية join إلى أيٍّ من الحدود التالية: تُراعي هذا الإعداد جميع قيمjoin_algorithm
القائمة على التجزئة، بما فيها تلك التي تكتب إلى القرص: فبلوغ الحد يوقف الاستعلام بدلًا من أن
يستدعي الكتابة إلى القرص. والاستثناء هو
legacy_join_size_limits_trigger_spilling: فعند تفعيله، يواصل الجزء من عملية join الذي
يعمل أصلًا على القرص الكتابة إليه بدلًا من التصرّف وفق هذا الإعداد.
كما تراعيه ie_join على الإدخال الذي تجمعه من كلا الجانبين. أما partial_merge فلا تزال
تتعامل مع هذه الحدود بتبديل الاستراتيجية — راجع
join_algorithm.
القيم الممكنة:
THROW— يطرح ClickHouse استثناءً ويوقف الاستعلام.BREAK— يوقف ClickHouse الاستعلام ولا يطرح استثناءً.
THROW.
انظر أيضًا