Skip to main content
يهدف هذا القسم إلى توضيح كيفية استخدام تقنيات مختلفة للأداء والتحسين، من خلال سيناريوهات شائعة، مثل المحلّل، وتنميط الاستعلامات، وتجنّب الأعمدة Nullable، بهدف تحسين أداء استعلامات ClickHouse.

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

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

اعتبارات عامة

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

مجموعة البيانات

سنستخدم مثالًا واقعيًا لتوضيح كيفية تناولنا لأداء الاستعلامات.  لنستخدم مجموعة بيانات NYC Taxi، التي تتضمن بيانات رحلات سيارات الأجرة في مدينة نيويورك. سنبدأ أولًا بإدخال مجموعة بيانات NYC Taxi من دون أي تحسينات. فيما يلي الأمر لإنشاء الجدول وإدراج البيانات من حاوية S3. لاحظ أننا نستنتج المخطط من البيانات عمدًا، وهذا ليس مُحسّنًا.
لنلقِ نظرة على مخطط الجدول التي استُنتجت تلقائيًا من البيانات.

حدِّد الاستعلامات البطيئة

سجلات الاستعلامات

يجمع ClickHouse تلقائيًا معلومات عن كل استعلام مُنفَّذ ويسجّلها في سجلات الاستعلامات. وتُخزَّن هذه البيانات في الجدول system.query_log لكل استعلام مُنفَّذ، يسجّل ClickHouse إحصاءات مثل وقت تنفيذ الاستعلام، وعدد الصفوف المقروءة، واستخدام الموارد مثل CPU، واستخدام الذاكرة، وعدد مرات الوصول إلى ذاكرة التخزين المؤقت لنظام الملفات.  لذلك، يُعَدّ سجل الاستعلام نقطة انطلاق جيدة عند التحقيق في الاستعلامات البطيئة. ويمكنك بسهولة رصد الاستعلامات التي تستغرق وقتًا طويلًا في التنفيذ وعرض معلومات استخدام الموارد لكل منها.  لنحدّد أطول خمسة استعلامات تشغيلًا في مجموعة بيانات سيارات الأجرة في NYC.
يشير الحقل query_duration_ms إلى المدة التي استغرقها تنفيذ ذلك الاستعلام المحدد. وبالنظر إلى النتائج في سجلات الاستعلامات، يمكننا أن نرى أن الاستعلام الأول يستغرق 2967ms للتنفيذ، وهو ما يمكن تحسينه.  قد ترغب أيضًا في معرفة الاستعلامات التي تُحمّل النظام أكثر من غيرها، وذلك من خلال فحص الاستعلام الذي يستهلك أكبر قدر من الذاكرة أو CPU. 
لنعزل الاستعلامات طويلة التشغيل التي عثرنا عليها ونعِد تشغيلها بضع مرات لفهم زمن الاستجابة.  في هذه المرحلة، من الضروري تعطيل ذاكرة التخزين المؤقت لنظام الملفات عبر ضبط الإعداد enable_filesystem_cache على 0 لتحسين إمكانية تكرار النتائج.
لنلخّص ذلك في الجدول لتسهيل القراءة. لنفهم بصورة أوضح قليلًا ما الذي تحققه هذه الاستعلامات. 
  • يحسب الاستعلام 1 توزيع المسافات للرحلات التي يزيد متوسط سرعتها على 30 ميلًا في الساعة.
  • يعثر الاستعلام 2 على عدد الرحلات ومتوسط تكلفتها أسبوعيًا. 
  • يحسب الاستعلام 3 متوسط مدة كل رحلة في مجموعة البيانات.
لا تتضمن أيّ من هذه الاستعلامات معالجةً شديدة التعقيد، باستثناء الاستعلام الأول، الذي يحسب مدة الرحلة آنيًا في كل مرة يُنفَّذ فيها الاستعلام. ومع ذلك، يستغرق كل واحد من هذه الاستعلامات أكثر من ثانية للتنفيذ، وهو وقت طويل جدًا في عالم ClickHouse. ويمكننا أيضًا ملاحظة استخدام الذاكرة في هذه الاستعلامات؛ فنحو 400 ميغابايت لكل استعلام يُعد مقدارًا كبيرًا جدًا. كذلك، يبدو أن كل استعلام يقرأ العدد نفسه من الصفوف (أي 329.04 مليونًا). دعونا نؤكد سريعًا عدد الصفوف الموجودة في هذا الجدول.
يحتوي الجدول على 329.04 مليون صف، لذلك ينفّذ كل استعلام مسحًا كاملًا للجدول.

جملة Explain

بعد أن أصبح لدينا بعض الاستعلامات طويلة الأمد، دعنا نفهم كيفية تنفيذها. لهذا الغرض، يدعم ClickHouse أمر EXPLAIN. وهو أداة بالغة الفائدة تتيح عرضًا تفصيليًا دقيقًا لجميع مراحل تنفيذ الاستعلام دون تشغيله فعليًا. وعلى الرغم من أن استيعابه قد يكون أمرًا مرهقًا لغير المتخصصين في ClickHouse، إلا أنه يظل أداةً لا غنى عنها للحصول على رؤية معمّقة حول كيفية تنفيذ استعلامك. تتضمن الوثائق دليلاً تفصيلياً حول ماهية عبارة EXPLAIN وكيفية استخدامها لتحليل تنفيذ الاستعلامات. وعوضاً عن تكرار ما ورد في هذا الدليل، سنركز على بعض الأوامر التي تساعد في تحديد نقاط الاختناق في أداء تنفيذ الاستعلامات. شرح الفهارس = 1 لنبدأ بـ EXPLAIN indexes = 1 لفحص خطة الاستعلام. خطة الاستعلام عبارة عن شجرة توضح كيفية تنفيذ الاستعلام، ويمكنك من خلالها معرفة الترتيب الذي ستُنفَّذ به بنود الاستعلام. وتُقرأ خطة الاستعلام التي تُعيدها عبارة EXPLAIN من الأسفل إلى الأعلى. لنجرّب استخدام أول استعلام من استعلاماتنا طويلة التشغيل.
المخرجات واضحة ومباشرة. يبدأ الاستعلام بقراءة البيانات من جدول nyc_taxi.trips_small_inferred، ثم يُطبَّق شرط WHERE لتصفية الصفوف استناداً إلى القيم المحسوبة. بعد ذلك، تُهيَّأ البيانات المصفَّاة للتجميع وتُحسَب الكوانتيلات. وأخيراً، تُرتَّب النتائج وتُعرَض. يمكننا هنا ملاحظة أنه لم يُستخدم أي مفتاح أساسي، وهذا أمر متوقع إذ لم نُعرِّف أيًّا منها عند إنشاء الجدول. ونتيجةً لذلك، يُجري ClickHouse فحصًا كاملًا للجدول لتنفيذ الاستعلام. شرح خط المعالجة يعرض EXPLAIN Pipeline استراتيجية التنفيذ الفعلية للاستعلام، ومن خلاله يمكنك معرفة كيف نفّذ ClickHouse فعليًا خطة الاستعلام العامة التي استعرضناها سابقًا.
يمكننا هنا ملاحظة عدد الخيوط المستخدمة لتنفيذ الاستعلام: 59 خيطًا، مما يشير إلى درجة عالية من التوازي. يُسرّع هذا من تنفيذ الاستعلام الذي كان سيستغرق وقتًا أطول على جهاز بموارد أقل. ويمكن لعدد الخيوط العاملة بالتوازي أن يُفسّر الحجم الكبير من الذاكرة التي يستهلكها الاستعلام. من الناحية المثالية، ينبغي أن تتبع الأسلوب ذاته في التحقيق مع جميع الاستعلامات البطيئة، للكشف عن خطط الاستعلام المعقدة غير الضرورية وفهم عدد الصفوف التي يقرأها كل استعلام والموارد التي يستهلكها.

المنهجية

قد يكون من الصعب تحديد الاستعلامات التي تسبّب مشكلات في بيئة الإنتاج، إذ يُحتمل أن يكون هناك عدد كبير من الاستعلامات التي تُنفَّذ في أي لحظة على عملية نشر ClickHouse لديك.  إذا كنت تعرف أي مستخدم أو قاعدة بيانات أو جداول تواجه مشكلات، فيمكنك استخدام الحقول user أو tables أو databases من system.query_logs لتضييق نطاق البحث.  بمجرد تحديد الاستعلامات التي تريد تحسينها، يمكنك البدء في العمل عليها. ومن الأخطاء الشائعة التي يرتكبها المطورون في هذه المرحلة تغيير عدة أشياء في الوقت نفسه وإجراء تجارب مخصّصة، ما ينتهي غالبًا إلى نتائج متباينة، والأهم من ذلك، من دون فهم واضح لما جعل الاستعلام أسرع.  يتطلب تحسين الاستعلامات منهجية منظَّمة. لا أتحدث هنا عن قياس أداء متقدم، بل إن وجود عملية بسيطة لفهم كيفية تأثير تغييراتك في أداء الاستعلامات يمكن أن يحقق فائدة كبيرة.  ابدأ بتحديد الاستعلامات البطيئة من سجلات الاستعلامات، ثم افحص التحسينات المحتملة كلًّا على حدة. وعند اختبار الاستعلام، تأكد من تعطيل ذاكرة التخزين المؤقت لنظام الملفات. 
يستفيد ClickHouse من التخزين المؤقت لتسريع أداء الاستعلامات في مراحل مختلفة. وهذا جيد لأداء الاستعلامات، لكنه أثناء استكشاف الأخطاء وإصلاحها قد يُخفي اختناقات I/O المحتملة أو مخطط جدول غير مناسب. لهذا السبب، أقترح إيقاف ذاكرة التخزين المؤقت لنظام الملفات أثناء الاختبار. وتأكد من إبقائها مفعّلة في بيئة الإنتاج.
بمجرد تحديد التحسينات المحتملة، يُوصى بتنفيذها واحدًا تلو الآخر لتتبّع كيفية تأثيرها في الأداء بشكل أفضل. يوجد أدناه مخطط يصف النهج العام. أخيرًا، انتبه إلى القيم الشاذة؛ فمن الشائع أن يعمل الاستعلام ببطء، إما لأن مستخدمًا نفّذ استعلامًا مكلّفًا مخصّصًا أو لأن النظام كان تحت ضغط لسبب آخر. يمكنك التجميع حسب الحقل normalized_query_hash لتحديد الاستعلامات المكلفة التي تُنفَّذ بانتظام. وغالبًا ما تكون هذه هي الاستعلامات التي ينبغي لك التحقيق فيها.

التحسين الأساسي

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

Nullable

كما هو موضح في وثائق أفضل الممارسات، تجنّب استخدام الأعمدة من النوع Nullable كلما أمكن. قد يبدو استخدامها بكثرة مغريًا لأنها تجعل آلية إدخال البيانات أكثر مرونة، لكنها تؤثر سلبًا في الأداء، إذ يجب معالجة عمود إضافي في كل مرة. ويمكن بسهولة، عبر تشغيل استعلام SQL يحسب الصفوف التي تحتوي على قيمة NULL، تحديد الأعمدة في جداولك التي تحتاج فعلًا إلى قيمة Nullable.
لدينا عمودان فقط يحتويان على قيم NULL: mta_tax وpayment_type. ولا ينبغي أن تستخدم بقية الحقول عمودًا من النوع Nullable.

انخفاض التعددية

من التحسينات السهلة التي يمكن تطبيقها على Strings تحقيق أقصى استفادة من نوع البيانات LowCardinality. وكما هو موضح في الوثائق الخاصة بـ low cardinality، يطبّق ClickHouse ترميز القاموس على أعمدة LowCardinality، مما يعزّز أداء الاستعلامات بشكل ملحوظ.  ومن القواعد العملية البسيطة لتحديد الأعمدة المناسبة لـ LowCardinality أن أي عمود يحتوي على أقل من 10,000 قيمة فريدة يُعد مرشحًا مثاليًا. يمكنك استخدام استعلام SQL التالي للعثور على الأعمدة ذات العدد المنخفض من القيم الفريدة.
نظرًا لانخفاض عدد القيم المميّزة، تُعدّ هذه الأعمدة الأربعة، ratecode_id وpickup_location_id وdropoff_location_id وvendor_id، مرشّحةً جيدةً لاستخدام نوع الحقل LowCardinality.

حسّن نوع البيانات

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

طبّق التحسينات

لنُنشئ جدولًا جديدًا لاستخدام البنية المُحسَّنة وإعادة إدخال البيانات.
نعيد تشغيل الاستعلامات باستخدام الجدول الجديد للتحقق من التحسن.  نلاحظ بعض التحسن في كلٍّ من وقت الاستعلام واستخدام الذاكرة. وبفضل تحسين بنية البيانات، ينخفض الحجم الإجمالي للبيانات التي تمثلها، مما يؤدي إلى تحسين استهلاك الذاكرة وتقليل وقت المعالجة.  لنتحقق من حجم الجداول لمعرفة الفرق. 
الجدول الجديد أصغر بكثير من الجدول السابق. نلاحظ انخفاضًا بنحو 34% في مساحة القرص الخاصة بالجدول (7.38 GiB مقابل 4.89 GiB).

أهمية المفاتيح الأساسية

تعمل المفاتيح الأساسية في ClickHouse بطريقة تختلف عن معظم أنظمة قواعد البيانات التقليدية. ففي تلك الأنظمة، تفرض المفاتيح الأساسية التفرّد وسلامة البيانات. وأي محاولة لإدراج قيم مكررة للمفتاح الأساسي تُرفض، وعادةً ما يُنشأ فهرس يعتمد على B-tree أو hash لإجراء بحث سريع.  أما في ClickHouse، فإن الغرض من المفتاح الأساسي مختلف؛ فهو لا يفرض التفرّد ولا يسهم في سلامة البيانات. وبدلاً من ذلك، صُمم لتحسين أداء الاستعلامات. ويحدد المفتاح الأساسي الترتيب الذي تُخزَّن به البيانات على القرص، ويُنفَّذ على شكل فهرس متناثر يخزّن مؤشرات إلى الصف الأول من كل حبيبة.
الحبيبات في ClickHouse هي أصغر وحدات البيانات التي تُقرأ أثناء تنفيذ الاستعلام. وتحتوي على عدد أقصى ثابت من الصفوف، يحدده index_granularity، وتبلغ قيمته الافتراضية 8192 صفًا. وتُخزَّن الحبيبات بشكل متجاور وتُرتَّب بحسب المفتاح الأساسي. 
ويُعد اختيار مجموعة مناسبة من المفاتيح الأساسية أمرًا مهمًا للأداء، بل ومن الشائع أيضًا تخزين البيانات نفسها في جداول مختلفة واستخدام مجموعات مختلفة من المفاتيح الأساسية لتسريع مجموعة معينة من الاستعلامات.  وتتيح لك خيارات أخرى يدعمها ClickHouse، مثل Projection أو العرض المادي، استخدام مجموعة مختلفة من المفاتيح الأساسية للبيانات نفسها. وسيتناول الجزء الثاني من سلسلة المدوّنة هذه ذلك بمزيد من التفصيل. 

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

يُعد اختيار المجموعة المناسبة من المفاتيح الأساسية موضوعًا معقدًا، وقد يتطلب بعض الموازنة والتجريب للوصول إلى أفضل تركيبة.  في الوقت الحالي، سنتبع هذه الممارسات البسيطة: 
  • استخدم الحقول التي يجري التصفية بناءً عليها في معظم الاستعلامات
  • اختر الأعمدة ذات عدد القيم المميزة الأقل أولًا 
  • ضع في اعتبارك تضمين مكوّن زمني في مفتاحك الأساسي، لأن التصفية حسب الوقت في مجموعة بيانات تحتوي على طابع زمني أمر شائع جدًا. 
في حالتنا، سنجرّب المفاتيح الأساسية التالية: passenger_count وpickup_datetime وdropoff_datetime عدد القيم المميزة للحقل passenger_count صغير (24 قيمة فريدة)، كما يُستخدم في استعلاماتنا البطيئة. ونضيف أيضًا حقول الطابع الزمني (pickup_datetime وdropoff_datetime) لأنها تُستخدم كثيرًا في التصفية. أنشئ جدولًا جديدًا بهذه المفاتيح الأساسية، ثم أعد إدخال البيانات.
ثم نُعيد تنفيذ استعلاماتنا. ونجمع نتائج التجارب الثلاث لرؤية التحسينات في الزمن المنقضي، والصفوف المعالجة، واستهلاك الذاكرة. 
الاستعلام 1
التشغيل 1التشغيل 2التشغيل 3
الزمن المنقضي1.699 sec1.353 sec0.765 sec
الصفوف المعالجة329.04 مليون329.04 مليون329.04 مليون
ذروة الذاكرة440.24 MiB337.12 MiB444.19 MiB
الاستعلام 2
التشغيل 1التشغيل 2التشغيل 3
الزمن المنقضي1.419 sec1.171 sec0.248 sec
الصفوف المعالجة329.04 مليون329.04 مليون41.46 مليون
ذروة الذاكرة546.75 MiB531.09 MiB173.50 MiB
الاستعلام 3
التنفيذ 1التنفيذ 2التنفيذ 3
الزمن المنقضي1.414 sec1.188 sec0.431 sec
الصفوف المُعالجة329.04 million329.04 million276.99 million
ذروة الذاكرة451.53 MiB265.05 MiB197.38 MiB
يمكننا ملاحظة تحسّن ملحوظ في زمن التنفيذ والذاكرة المستخدمة على جميع الأصعدة.  يستفيد الاستعلام 2 أكثر من المفتاح الأساسي. لنلقِ نظرة على كيف تختلف خطة الاستعلام المتولدة عمّا كانت عليه سابقًا.
بفضل المفتاح الأساسي، لم يُحدَّد إلا جزء فرعي من حبيبات الجدول. وهذا وحده يحسّن أداء الاستعلام بدرجة كبيرة، لأن ClickHouse لا يحتاج إلا إلى معالجة قدر أقل بكثير من البيانات.

الخطوات التالية

نأمل أن يكون هذا الدليل قد ساعدك على فهم كيفية استقصاء الاستعلامات البطيئة في ClickHouse وكيفية تسريعها. وللتعمق أكثر في هذا الموضوع، يمكنك قراءة المزيد عن محلل الاستعلام والتنميط لفهم كيفية تنفيذ ClickHouse لاستعلامك بصورة أدق. ومع ازدياد إلمامك بخصائص ClickHouse، أوصي بقراءة المزيد عن مفاتيح التقسيم وفهارس تخطي البيانات للتعرّف على تقنيات أكثر تقدمًا يمكنك استخدامها لتسريع استعلاماتك.
آخر تعديل في ٢٣ يوليو ٢٠٢٦