المقدمة
- إعادة ترتيب كاملة
- مجموعة فرعية من الجدول الأصلي بترتيب مختلف
- تجميع محسوب مسبقًا (يشبه العرض المادي) ولكن بترتيب يتوافق مع التجميع.
كيف تعمل الإسقاطات؟
- الاستخدام الصحيح للفهارس الأساسية
- الحساب المسبق للتجميعات
تخزين أكثر ذكاءً باستخدام _part_offset
_part_offset في
الإسقاطات، مما يوفّر طريقة جديدة لتعريف الإسقاط.
توجد الآن طريقتان لتعريف الإسقاط:
- تخزين الأعمدة الكاملة (السلوك الأصلي): يحتوي الإسقاط على البيانات الكاملة ويمكن قراءته مباشرةً، مما يوفّر أداءً أسرع عندما تتوافق عوامل التصفية مع مفتاح الفرز الخاص بالإسقاط.
-
تخزين مفتاح الفرز +
_part_offsetفقط: يعمل الإسقاط مثل فهرس. يستخدم ClickHouse الفهرس الأساسي الخاص بالإسقاط لتحديد الصفوف المطابقة، لكنه يقرأ البيانات الفعلية من الجدول الأساسي. ويقلّل ذلك من العبء الإضافي للتخزين مقابل زيادة طفيفة في I/O وقت تنفيذ الاستعلام.
_part_offset.
متى تستخدم الإسقاطات؟
- لا تسمح الإسقاطات باستخدام TTL مختلف للجدول المصدر والجدول الهدف (المخفي)، بينما تتيح العروض المادية استخدام قيم TTL مختلفة.
- لا تدعم الجداول التي تحتوي على إسقاطات التحديثات الخفيفة وعمليات الحذف.
- يمكن ربط العروض المادية في سلسلة: إذ يمكن أن يكون الجدول الهدف لعرض مادي واحد هو الجدول المصدر لعرض مادي آخر، وهكذا. وهذا غير ممكن مع الإسقاطات.
- لا تدعم تعريفات الإسقاطات عمليات JOIN، لكن العروض المادية تدعمها. ومع ذلك، يمكن للاستعلامات على الجداول التي تحتوي على إسقاطات استخدام joins بحرية.
- لا تدعم تعريفات الإسقاطات المرشحات (عبارة
WHERE)، لكن العروض المادية تدعمها. ومع ذلك، يمكن للاستعلامات على الجداول التي تحتوي على إسقاطات استخدام المرشحات بحرية.
- تكون هناك حاجة إلى إعادة ترتيب كاملة للبيانات. وبينما يمكن للتعبير في
الإسقاط، من الناحية النظرية، استخدام
GROUP BY,فإن العروض المادية تكون أكثر فعالية في الاحتفاظ بالتجميعات. كما أن مُحسِّن الاستعلامات يكون على الأرجح أكثر ميلًا إلى الاستفادة من الإسقاطات التي تستخدم إعادة ترتيب بسيطة، أيSELECT * ORDER BY x. ويمكنك تحديد مجموعة فرعية من الأعمدة في هذا التعبير لتقليل البصمة التخزينية. - يكون المستخدمون مرتاحين للزيادة المحتملة في البصمة التخزينية وللكلفة الإضافية المترتبة على كتابة البيانات مرتين. اختبر التأثير على سرعة الإدراج و قيّم عبء التخزين.
أمثلة
التصفية على أعمدة ليست ضمن المفتاح الأساسي
pickup_datetime.
لنكتب استعلامًا بسيطًا للعثور على جميع معرّفات الرحلات التي قدّم فيها الركاب
إكرامية للسائق تزيد على 200 دولار:
لاحظ أنه لأننا نُجري التصفية على tip_amount، وهو ليس ضمن ORDER BY، اضطر ClickHouse
إلى إجراء مسح كامل للجدول. لنعمل الآن على تسريع هذا الاستعلام.
وللحفاظ على الجدول الأصلي والنتائج الأصلية، سننشئ جدولًا جديدًا وننسخ البيانات باستخدام INSERT INTO SELECT:
ALTER TABLE مع تعليمة ADD PROJECTION
التالية:
MATERIALIZE PROJECTION
لكي تُرتَّب البيانات فيه فعليًا وتُعاد كتابتها وفقًا
للاستعلام المحدد أعلاه:
system.query_log:
استخدام الإسقاط لتسريع استعلامات أسعار العقارات المدفوعة في المملكة المتحدة
town وprice لم يكونا ضمن عبارة ORDER BY عند
إنشاء الجدول:
INSERT INTO SELECT:
prj_oby_town_price، الذي يُنتج
جدولًا إضافيًا (مخفيًا) بفهرس أساسي، مرتّبًا حسب البلدة والسعر، من أجل
تحسين الاستعلام الذي يسرد المقاطعات في بلدة محددة بحسب أعلى الأسعار
المدفوعة:
mutations_sync
لفرض التنفيذ بشكل متزامن.
ننشئ ونملأ الإسقاط prj_gby_county — وهو جدول إضافي (مخفي)
يحسب مسبقًا وبشكل تزايدي قيم التجميع avg(price) لجميع
مقاطعات المملكة المتحدة البالغ عددها 130:
إذا كانت هناك عبارة
GROUP BY مستخدمة في إسقاط مثل الإسقاط prj_gby_county
أعلاه، فإن محرك التخزين الأساسي للجدول (المخفي)
يصبح AggregatingMergeTree، وتُحوَّل جميع الدوال التجميعية إلى
AggregateFunction. وهذا يضمن تجميع البيانات التزايدي بشكل صحيح.uk_price_paid_with_projections
والإسقاطين التابعين له:
إذا شغّلنا الآن الاستعلام الذي يسرد المقاطعات في لندن ذات أعلى ثلاثة
أسعار مرة أخرى، فسنلاحظ تحسنًا في أداء الاستعلام:
وبالمثل، بالنسبة إلى الاستعلام الذي يسرد مقاطعات المملكة المتحدة ذات أعلى
ثلاثة متوسطات للأسعار المدفوعة:
لاحظ أن كلا الاستعلامين يستهدفان الجدول الأصلي، وأن كليهما أسفر
عن فحص كامل للجدول (إذ جرى بثّ جميع الصفوف البالغ عددها 30.03 مليون صف من القرص) قبل أن
ننشىء الإسقاطين.
لاحظ أيضًا أن الاستعلام الذي يسرد المقاطعات في لندن لأعلى ثلاثة
أسعار مدفوعة يبثّ 2.17 مليون صف. وعندما استخدمنا مباشرةً جدولًا ثانيًا
مُحسّنًا لهذا الاستعلام، لم يُبثّ سوى 81.92 ألف صف من القرص.
يرجع سبب هذا الاختلاف إلى أن التحسين optimize_read_in_order
المذكور أعلاه غير مدعوم حاليًا للإسقاطات.
نفحص جدول system.query_log لنرى أن ClickHouse
استخدم تلقائيًا الإسقاطين للاستعلامين المذكورين أعلاه (انظر
عمود projections أدناه):
أمثلة إضافية
CREATE AS وINSERT INTO SELECT.
إنشاء إسقاط
toYear(date) وdistrict وtown:
optimize_use_projections، وهو مُمكَّن افتراضيًا.
الاستعلام 1. متوسط السعر لكل سنة
الاستعلام 2. متوسط السعر سنويًا في لندن
الاستعلام 3. الأحياء الأكثر تكلفة
(date >= '2020-01-01') بحيث يطابق بُعد الإسقاط (toYear(date) >= 2020):
مرة أخرى، النتيجة هي نفسها، لكن لاحظ تحسّن أداء الاستعلام في الاستعلام الثاني.
دمج الإسقاطات في استعلام واحد
_part_offset الذي أُضيف في
الإصدار السابق، يمكن لـ ClickHouse الآن استخدام عدة إسقاطات لتسريع
استعلام واحد يتضمن عدة عوامل تصفية.
ومن المهم أن ClickHouse لا يزال يقرأ البيانات من إسقاط واحد فقط (أو من الجدول الأساسي)،
لكنه يستطيع استخدام الفهارس الأساسية للإسقاطات الأخرى لاستبعاد الأجزاء غير الضرورية قبل القراءة.
ويكون هذا مفيدًا بشكل خاص للاستعلامات التي تُجري تصفية على عدة أعمدة، إذ قد
يطابق كلٌّ منها إسقاطًا مختلفًا.
حاليًا، تقتصر هذه الآلية على استبعاد الأجزاء بالكامل. أما الاستبعاد على مستوى granule فغير مدعوم بعد.لتوضيح ذلك، نعرّف الجدول (مع إسقاطات تستخدم أعمدة
_part_offset)
ونُدرج خمسة صفوف كمثال تطابق المخططات أعلاه.
ملاحظة: يستخدم الجدول إعدادات مخصصة لأغراض التوضيح، مثل حبيبات من صف واحد
وتعطيل عمليات دمج الأجزاء، وهي غير موصى بها للاستخدام في بيئات الإنتاج.
- خمسة أجزاء منفصلة (جزء واحد لكل صف مُدرَج)
- مُدخل واحد في الفهرس الأساسي لكل صف (في الجدول الأساسي وفي كل إسقاط)
- يحتوي كل جزء على صف واحد بالضبط
region وuser_id.
ونظرًا إلى أن الفهرس الأساسي للجدول الأساسي مبني على event_date وid، فهو
غير مفيد هنا، لذا يستخدم ClickHouse ما يلي:
region_projلتقليص الأجزاء حسب المنطقةuser_id_projلتقليص الأجزاء أكثر حسبuser_id
EXPLAIN projections = 1، الذي يوضّح كيف
يحدّد ClickHouse الإسقاطات ويطبّقها.
EXPLAIN (المعروض أعلاه) خطة الاستعلام المنطقية من الأعلى إلى الأسفل:
في النهاية، لا تتم القراءة إلا من جزء واحد فقط من أصل 5 أجزاء في الجدول الأساسي.
ومن خلال الجمع بين تحليل الفهارس لعدة إسقاطات، يقلّل ClickHouse بشكل كبير كمية البيانات التي تُفحص،
مما يحسّن الأداء مع إبقاء أعباء التخزين الإضافية منخفضة.