التحقق من المفتاح الأساسي
قد يلاحظ المستخدمون أحيانًا أن استعلاماتهم أبطأ من المتوقع، رغم اعتقادهم أنهم يجرون الترتيب أو التصفية باستخدام مفتاح أساسي. في هذه المقالة نوضح كيف يمكنهم التأكد من استخدام المفتاح، مع تسليط الضوء على الأسباب الشائعة لعدم استخدامه.
إنشاء جدول
لنأخذ الجدول البسيط التالي:
CREATE TABLE logs
(
`code` LowCardinality(String),
`timestamp` DateTime64(3)
)
ENGINE = MergeTree
ORDER BY (code, toUnixTimestamp(timestamp))لاحظ كيف يتضمّن مفتاح الترتيب لدينا toUnixTimestamp(timestamp) بوصفه العنصر الثاني.
تعبئة البيانات
عبّئ هذا الجدول بـ 100 مليون صف:
INSERT INTO logs SELECT
['200', '404', '502', '403'][toInt32(randBinomial(4, 0.1)) + 1] AS code,
now() + toIntervalMinute(number) AS timestamp
FROM numbers(100000000)
0 rows in set. Elapsed: 15.845 sec. Processed 100.00 million rows, 800.00 MB (6.31 million rows/s., 50.49 MB/s.)
SELECT count()
FROM logs
┌التصفية الأساسية
إذا قمنا بالتصفية حسب code، فسنرى في الناتج عدد الصفوف التي جرى فحصها: 49.15 thousand. لاحظ أن هذا يمثّل مجموعة فرعية من إجمالي 100m صف.
SELECT count() AS c
FROM logs
WHERE code = '200'
┌علاوة على ذلك، يمكننا أيضًا التأكد من استخدام الفهرس عبر البند EXPLAIN indexes=1:
EXPLAIN indexes = 1
SELECT count() AS c
FROM logs
WHERE code = '200'
┌لاحظ كيف أن عدد الـ حبيبات التي جرى فحصها، 8012، لا يمثّل سوى جزء من الإجمالي 12209. ويؤكد القسم المميز أدناه استخدام شيفرة المفتاح الأساسي.
PrimaryKey
Keys:
code تُعدّ الحبيبات وحدة معالجة البيانات في ClickHouse، وعادةً ما تحتوي كل واحدة منها على 8192 صفًا. ولمزيد من التفاصيل حول الحبيبات وكيفية ترشيحها، نوصي بقراءة هذا الدليل.
التصفية باستخدام مفاتيح متعددة
لنفترض أننا نُجري التصفية وفقًا لـcode وtimestamp:
SELECT count()
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
┌في هذه الحالة، يُستخدم مفتاحا الترتيب كليهما لتصفية الصفوف، ما يعني الحاجة إلى قراءة 87 حبيبة فقط.
استخدام المفاتيح في الفرز
يمكن لـ ClickHouse أيضًا الاستفادة من مفاتيح الترتيب لتنفيذ الفرز بكفاءة. وتحديدًا،
عندما يكون الإعداد optimize_read_in_order مُمكّنًا (وهو كذلك افتراضيًا)، يستخدم خادم ClickHouse فهرس الجدول ويقرأ البيانات وفقًا لترتيب مفتاح ORDER BY. يتيح ذلك تجنّب قراءة جميع البيانات عند تحديد LIMIT. لذلك، تُعالَج الاستعلامات على مجموعات البيانات الكبيرة ذات الحدود الصغيرة بسرعة أكبر. راجع هنا وهنا لمزيد من التفاصيل.
ومع ذلك، يتطلب هذا تطابق المفاتيح المستخدمة.
على سبيل المثال، انظر إلى هذا الاستعلام:
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY timestamp ASC
LIMIT 10
┌يمكننا التأكد من عدم الاستفادة من هذا التحسين هنا باستخدام EXPLAIN pipeline:
EXPLAIN PIPELINE
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY timestamp ASC
LIMIT 10
┌السطر MergeTreeSelect(pool: ReadPool, algorithm: Thread) هنا لا يدل على استخدام هذا التحسين، بل على عملية قراءة عادية. ويرجع ذلك إلى أن مفتاح ترتيب الجدول لدينا يستخدم toUnixTimestamp(Timestamp) وليس timestamp. ويؤدي تصحيح هذا الاختلاف إلى حل المشكلة:
EXPLAIN PIPELINE
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY toUnixTimestamp(timestamp) ASC
LIMIT 10
┌