يصبح تحسين الاستعلام أسهل عند تغيير جزء واحد منه في كل مرة ومقارنة النتائج بخط أساس ثابت. يوضح هذا الدليل كيفية تبسيط الاستعلام تدريجيًا واستخدام الفروق بين عمليات التشغيل لتحديد العمليات الأكثر إسهامًا في مدة تنفيذه. يمكنك بعد ذلك التحقق من عنق الزجاجة المحتمل قبل اختيار التحسين المناسب.
قبل أن تبدأ
ابدأ بنمط متكرر من الاستعلامات البطيئة التي تريد التحقيق فيها. إذا لم تكن قد حددت نمطًا بعد، فراجع تشخيص الاستعلامات البطيئة للتعرّف على الخطوات.
لتشغيل الأمثلة الواردة في هذا الدليل كما كُتبت، أنشئ الجدول nyc_taxi.trips_small_inferred وحمّله إذا لم تكن قد فعلت ذلك مسبقًا:
إعداد مجموعة البيانات النموذجية
CREATE DATABASE IF NOT EXISTS nyc_taxi;
USE nyc_taxi;
CREATE TABLE nyc_taxi.trips_small_inferred
ORDER BY () EMPTY
AS SELECT *
FROM s3(
'https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/clickhouse-academy/nyc_taxi_2009-2010.parquet',
NOSIGN,
Parquet
);
INSERT INTO nyc_taxi.trips_small_inferred
SELECT *
FROM s3(
'https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/clickhouse-academy/nyc_taxi_2009-2010.parquet',
NOSIGN,
Parquet
);يستخدم الجدول النموذجي ORDER BY ()، لذا لا يمكن لفلتر التاريخ فيه استخدام مفتاح ترتيب لاستبعاد البيانات أثناء القراءة. استخدم المثال للتدرّب على منهجية المقارنة، لا باعتباره معيارًا للأداء.
آلية العمل
يتيح لك تبسيط الاستعلام تدريجيًا مقارنة مدته قبل إزالة إحدى مراحل المعالجة وبعدها. تساعدك الفروق على تحديد ما إذا كان ينبغي التحقيق في الفحص والتصفية والتجميع والحسابات التجميعية أو العمليات اللاحقة، مثل الفرز وتنسيق المخرجات:
- شغّل الاستعلام الأصلي لتحديد قياسات خط الأساس.
- احتفظ بـ
GROUP BY، واستبدل الحسابات التجميعية في الاستعلام بـcount، وأزل العمليات اللاحقة، مثل الفرز وتنسيق المخرجات. - أزل التجميع وشغّل
countدون تجميع لتقدير مقدار العمل المتبقي من الفحص والتصفية وأي عمليات ربط.
تنطبق هذه المراحل مباشرةً على استعلامات التجميع التقليدية التي تستخدم التجميع حسب المجموعات. بالنسبة إلى الاستعلامات الأكثر تعقيدًا، طبّق المبدأ نفسه على كتلة SELECT واحدة في كل مرة: احتفظ بمصادر البيانات وعوامل التصفية المكافئة، وأزل عملية واحدة في كل مرة، وتحقّق من خطة التنفيذ بعد كل تغيير.
وضع خط أساس قابل للتكرار
استخدم الممارسات التالية لجعل القياسات قابلة للمقارنة:
- أبقِ عبارات
FROMوJOINوPREWHEREوWHEREدون تغيير، لضمان استخدام البيانات والنطاق الزمني نفسيهما في كل مقارنة. - شغّل كل إصدار من الاستعلام عدة مرات تحت حمل نظام مماثل.
- حافظ على اتساق ظروف التخزين المؤقت. إما شغّل كل إصدار من الاستعلام قبل تسجيل القياسات، أو عطّل الذواكر المؤقتة المدرجة أدناه. لا تقارن بين عمليات التشغيل المخزنة مؤقتًا وغير المخزنة مؤقتًا.
- سجّل مدةً ممثلة، مثل الوسيط لعمليات التشغيل المتكررة بعد أي عمليات إحماء، بدلًا من الاعتماد على أسرع نتيجة أو أبطئها.
- غيّر متغيرًا واحدًا في كل مرة لكي تتمكن من ربط فرق الأداء بتغيير محدد.
لإجراء مقارنة تشخيصية دون استخدام التخزين المؤقت، عطّل ذاكرة ClickHouse المؤقتة لنظام الملفات للبيانات البعيدة، وذاكرة الاستعلام المؤقتة، وذاكرة التخزين المؤقت لشروط الاستعلام. عطّل الإسقاطات الضمنية أيضًا، حتى لا يستخدم count في التشغيل C خطة تنفيذ محسّنة تتجاوز الفحص الذي تنوي مقارنته.
SET enable_filesystem_cache = 0;
SET use_query_cache = 0;
SET use_query_condition_cache = 0;
SET optimize_use_implicit_projections = 0;يجمع سير العمل بين عمليات تشغيل استعلامات مضبوطة وقياسات من سجل الاستعلامات:

اجمع القياسات لكل عملية تشغيل كما يلي:
-
عيّن معرّف استعلام فريدًا لكل عملية تشغيل، أو سجّل المعرّف الذي تنشئه واجهة الاستعلام. على سبيل المثال، سمِّ عمليات التشغيل المتكررة:
bottleneck-a-1وbottleneck-a-2وbottleneck-a-3. عند استخدامclickhouse-client، مرّر--query_id your-query-idعند تنفيذ استعلام. -
نفّذ كل استعلام مقارنة عدة مرات في الظروف نفسها. افصل عمليات تشغيل الإحماء عن عمليات التشغيل المقاسة.
-
أفرغ سجل الاستعلامات قبل البحث عن الاستعلامات المكتملة مؤخرًا:
SYSTEM FLUSH LOGS;إذا لم تتمكن من تشغيل
SYSTEM FLUSH LOGS، فانتظر حتى يُفرغ سجل الاستعلامات تلقائيًا، ثم أعد محاولة البحث. إذا لم يظهر السجل مطلقًا، فتحقق من تمكين تسجيل الاستعلامات، ومن قدرتك على قراءةsystem.query_log، ومن أنك تستعلم عن العقدة التي نفّذت الاستعلام. -
ابحث عن السجل المكتمل لكل معرّف استعلام. يسجل
system.query_logحدثَيQueryStartوQueryFinishلكل استعلام مكتمل. رشّح حسبQueryFinish، الذي يحتوي على المدة النهائية والصفوف والبايتات المقروءة وذروة استخدام الذاكرة:SELECT query_id, query_duration_ms, read_rows, read_bytes, memory_usage FROM system.query_log WHERE type = 'QueryFinish' AND query_id = 'your-query-id' ORDER BY event_time_microseconds DESC LIMIT 1; -
لكل إصدار من الاستعلام، استخدم المدة الوسيطة لعمليات التشغيل المقاسة. سجّل
read_rowsوread_bytesوذروة استخدام الذاكرة من عملية التشغيل الأقرب إلى هذا الوسيط، حتى تظل القياسات مرتبطة بعملية تشغيل فعلية.
استخدم جدولًا مثل التالي لتنظيم القياسات التمثيلية. راجع system.query_log لمزيد من المعلومات عن حقوله وإعداداته.
| التشغيل | إصدار الاستعلام | المدة التمثيلية | read_rows |
read_bytes |
ذروة استخدام الذاكرة |
|---|---|---|---|---|---|
| A | الاستعلام الأصلي | ||||
| B | count مجمّع |
||||
| C | count غير مجمّع |
Run,Query version,Representative duration,read_rows,read_bytes,Peak memory
A,Original query,,,,
B,Grouped count,,,,
C,Ungrouped count,,,,نفِّذ استعلامات أبسط تدريجيًا
لتوضيح المقارنات الثلاث، يستخدم المثال عبء العمل المجمّع حسب نطاق زمني. يمكنك تطبيق هذه الطريقة على استعلام آخر من دون اتباع المثال خطوةً بخطوة. إذا لم يتضمن الاستعلام GROUP BY، فتجاوز التشغيل B كما هو موضح أدناه.
التشغيل A: قياس الاستعلام الأصلي
نفِّذ الاستعلام كاملًا من دون تغيير عوامل التصفية أو التجميع أو التعبيرات التجميعية أو الفرز أو المخرجات. يحدد ذلك مدة خط الأساس، وعدد الصفوف والبايتات المقروءة، وذروة استخدام الذاكرة.
يجمع هذا الاستعلام الرحلات حسب نوع الدفع ويحسب عدة قيم تجميعية:
SELECT
payment_type,
count() AS trip_count,
formatReadableQuantity(sum(trip_distance)) AS total_distance,
avg(total_amount) AS total_amount_avg,
avg(tip_amount) AS tip_amount_avg
FROM nyc_taxi.trips_small_inferred
WHERE pickup_datetime >= '2009-01-01'
AND pickup_datetime < '2009-04-01'
GROUP BY payment_type
ORDER BY trip_count DESC;سجّل قياسات الاستعلام باعتبارها التشغيل A.
التشغيل B: الاحتفاظ بالتجميع مع count
احتفظ بعبارات FROM وJOIN وPREWHERE وWHERE ومفاتيح التجميع في الاستعلام. استبدل تعبيراته التجميعية بـ count مجمّع. أزل المعالجة التي تلي التجميع، بما في ذلك الفرز الأصلي وتعبيرات المخرجات.
SELECT
payment_type,
count() AS trip_count
FROM nyc_taxi.trips_small_inferred
WHERE pickup_datetime >= '2009-01-01'
AND pickup_datetime < '2009-04-01'
GROUP BY payment_type;لا يزال التشغيل B يفحص البيانات ويصفيها، وينفذ أي عمليات ربط، ويكوّن المجموعات. قارن مدته بالتشغيل A لتقدير إسهام التعبيرات التجميعية الأصلية والمعالجة التي تلي التجميع. وقارن أيضًا read_bytes، لأن إزالة التعبيرات التجميعية قد تلغي الحاجة إلى قراءة بعض الأعمدة.
إذا لم يتضمن الاستعلام الأصلي GROUP BY، فلا توجد مرحلة تجميع لعزلها. تجاوز التشغيل B وقارن الاستعلام الأصلي مباشرةً بالتشغيل C.
التشغيل C: إزالة التجميع
أزل GROUP BY وأرجع قيمة count واحدة. أبقِ عبارات FROM وJOIN وPREWHERE وWHERE من دون تغيير كي تكون المعالجة المتبقية قابلة للمقارنة.
SELECT count()
FROM nyc_taxi.trips_small_inferred
WHERE pickup_datetime >= '2009-01-01'
AND pickup_datetime < '2009-04-01';يوفر التشغيل C خط أساس للعمليات التي تحتفظ بها خطة تنفيذه، وليس قياسًا معزولًا للفحص أو التصفية. قارنه بالتشغيل B لتقدير إسهام التجميع. وقارن أيضًا read_bytes، لأن إزالة مفتاح التجميع قد تقلل عدد الأعمدة المقروءة. توضح قيمة count المُعادة عدد الصفوف التي تصل إلى التجميع بعد عوامل التصفية وعمليات الربط المحتفَظ بها.
قبل تفسير نتائج التشغيل C، تأكد من أن خطة تنفيذه تقرأ مصدر البيانات المقصود وتطبق عوامل التصفية المحتفَظ بها. قد يغيّر إسقاط أو count يعتمد على البيانات الوصفية طبيعة العمل المنفذ. للحصول على خط أساس قائم على الفحص، عطّل التحسين الظاهر في الخطة في عمليات التشغيل الثلاثة جميعها: استخدم optimize_use_implicit_projections = 0 لإسقاط ضمني، أو optimize_use_projections = 0 لإسقاط صريح، أو optimize_trivial_count_query = 0 لـ count غير مقيّد يُستخرج من البيانات الوصفية للجدول.
إذا ظل التشغيل C بطيئًا، فتحقق من العمليات التي يحتفظ بها، بدءًا بالفحص والتصفية. استخدم سجلات الاستعلامات وEXPLAIN للتحقق من عنق الزجاجة المشتبه به قبل تغيير الاستعلام.
تفسير الفروقات
قارن مددًا ممثّلة من عمليات تشغيل متكررة بدلًا من طرح توقيتين منفردين. تشير الفروقات الكبيرة والمتسقة إلى مواضع التحقيق التالية:
| الملاحظة | الاختناقات المحتملة | التحقيق التالي |
|---|---|---|
| التشغيل A أبطأ بكثير من التشغيل B | تعبيرات التجميع، والفرز، وأعمال أخرى بعد التجميع، أو قراءة أعمدة إضافية | افحص دوال التجميع المكلفة والتعبيرات وORDER BY وread_bytes وذروة استخدام الذاكرة |
| التشغيل B أبطأ بكثير من التشغيل C | التجميع، أو عدد العناصر المميزة للمجموعات، أو قراءة مفاتيح التجميع | افحص مفاتيح التجميع، وعدد المجموعات، وread_bytes، وذروة استخدام الذاكرة |
| يظل التشغيل C بطيئًا | الفحص، أو التصفية، أو عمليات الربط، أو عملية أخرى يحتفظ بها التشغيل C | افحص الصفوف والبايتات المقروءة، واستخدام المفتاح الأساسي، وفهارس تخطي البيانات، وخطة التنفيذ؛ ثم تحقّق من الاختناق المشتبه به |
| للعمليات الثلاث مدد متشابهة | قد يكون مصدر زمن الاستجابة مشتركًا بين الإصدارات الثلاثة، أو ربما غيّر التبسيط خطة التنفيذ | قارن read_rows وread_bytes وذروة استخدام الذاكرة بين عمليات التشغيل. إذا كانت متشابهة أيضًا، فحقّق في العمليات التي يحتفظ بها التشغيل C. وإلا، فقارن خطط التنفيذ بحثًا عن الفروقات |
قارن الصفوف المقروءة بنتيجة count
قارن قيمة read_rows للتشغيل C بالقيمة التي تُرجعها count. على سبيل المثال، إذا كانت read_rows تساوي 100 مليون وأرجعت count مليونًا واحدًا، فهذا يعني أن ClickHouse فحص نحو 100 صف مصدر لكل صف تم عده. يشير ذلك إلى أن عامل التصفية استبعد معظم الصفوف المقروءة من الجدول، لكنه لا يوضح السبب. هذه النسبة مخصصة لعمليات الفحص البسيطة لجدول واحد. أما في الاستعلامات التي تتضمن مصادر بيانات متعددة أو إسقاطات، ففسّر read_rows باستخدام خطة التنفيذ بدلًا من ذلك.
في ClickHouse 25.9 والإصدارات الأحدث، عطّل ذاكرة التخزين المؤقت لشرط الاستعلام والتطبيق الديناميكي لفهارس تخطي البيانات قبل فحص استخدام الفهارس:
SET use_query_condition_cache = 0;
SET use_skip_indexes_on_data_read = 0;ثم استخدم EXPLAIN indexes = 1 لمعرفة الفهارس التي استخدمها ClickHouse، وعدد الأجزاء والحبيبات التي استبعدها كل فهرس. إذا اختار ClickHouse حبيبات أكثر من المتوقع، فتحقق مما إذا كانت عوامل التصفية تتوافق مع مفتاح ترتيب الجدول، وما إذا كان استبعاد الأقسام أو فهرس تخطي البيانات قد يستبعد مزيدًا من الحبيبات. إذا لم تتضمن الخطة قسم Indexes، فهذا يعني أن EXPLAIN لم يُظهر استبعاد الفهارس لهذا الاستعلام. أما الاستعلام التحليلي الذي يفحص الجدول بأكمله، فمن المتوقع أن يقرأ معظم بيانات الجدول.
تحقّق من عنق الزجاجة المُشتبه به
بعد أن تشير المقارنة إلى وجود عنق زجاجة محتمل، تحقّق منه قبل تغيير المخطط أو الاستعلام. استخدم الأدلة المناسبة لمصدر زمن الاستجابة المُشتبه به:
- إذا كان عنق الزجاجة في الفحص أو التصفية، فاستخدم
EXPLAIN indexes = 1مع الإعدادات الموضحة أعلاه لمعرفة الفهارس التي يستخدمها ClickHouse، وعدد الأجزاء والحبيبات التي يستبعدها كل فهرس. تحقّق مما إذا كانت الخطة تستخدم إسقاطًا ضمنيًا بدلًا من الفحص المتوقع. - إذا كان عنق الزجاجة في التجميع حسب المجموعات أو التجميع، فافحص أحداث ملف تعريف الاستعلام ذات الصلة وذروة استخدام الذاكرة.
- إذا ظل التشغيل C بطيئًا ويحتوي على عمليات ربط، فقارنه باستعلام تشخيصي يزيل عملية ربط واحدة في كل مرة. يشير الانخفاض الكبير في المدة إلى أن عملية الربط المُزالة تسهم بقدر كبير من العمل. ولأن إزالة عملية ربط تغيّر معنى الاستعلام، فاستخدم هذه المقارنة لعزل التوقيت فقط، وفسّر التغييرات في عدد الصفوف بشكل منفصل.
- إذا كان عنق الزجاجة في عملية أخرى يحتفظ بها التشغيل C، فافحص خطة التنفيذ وأحداث ملف تعريف الاستعلام ذات الصلة.
راجع دليل تشخيص الاستعلامات البطيئة للاطلاع على تفاصيل معلومات الفهرس التي يعرضها EXPLAIN. طبّق تغييرًا مستهدفًا واحدًا، ثم كرر عمليات التشغيل A وB وC في الظروف نفسها. تأكّد من أن التغيير قلّل العمل المستهدف ولم ينقل عنق الزجاجة إلى موضع آخر.
الخطوات التالية
تابع إلى أساليب التحسين لمطابقة عنق الزجاجة المُحتمل مع تغيير مستهدف واحد أو أكثر.