Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

محرك الجدول TimeSeries

ميزة تجريبية
غير مدعوم في ClickHouse Cloud

محرك الجدول يخزّن سلسلة زمنية، أي مجموعة من القيم المرتبطة بطوابع زمنية ووسوم (أو تسميات):

metric_name1[tag1=value1, tag2=value2, ...] = {timestamp1: value1, timestamp2: value2, ...}
metric_name2[...] = ...

الصياغة

CREATE TABLE name [(columns)] ENGINE=TimeSeries
[SETTINGS var1=value1, ...]
[SAMPLES db.samples_table_name | [SAMPLES INNER COLUMNS (...)] [SAMPLES INNER ENGINE engine(arguments)]]
[TAGS db.tags_table_name | [TAGS INNER COLUMNS (...)] [TAGS INNER ENGINE engine(arguments)]]
[METRICS db.metrics_table_name | [METRICS INNER COLUMNS (...)] [METRICS INNER ENGINE engine(arguments)]]

الاستخدام

من الأسهل البدء بالإعدادات الافتراضية لكل شيء (يُسمح بإنشاء جدول TimeSeries من دون تحديد قائمة بالأعمدة):

CREATE TABLE my_table ENGINE=TimeSeries

بعد ذلك، يمكن استخدام هذا الجدول مع البروتوكولات التالية (يجب تعيين منفذ في تهيئة الخادم):

الأعمدة الخارجية

تُنشأ أعمدة جدول TimeSeries تلقائيًا. وهذه هي الأعمدة الخارجية؛ فهي لا تخزّن أي بيانات، وإنما توفّر فقط واجهة لـ SELECT/INSERT. وتُخزَّن البيانات الفعلية في الجداول الهدف. وفيما يلي قائمة الأعمدة الخارجية:

Name Type Description
metric_name String اسم المقياس
tags Map(String, String) خريطة الوسوم (التسميات) للسلسلة الزمنية
time_series Array(Tuple(DateTime64(3), Float64)) افتراضيًا مصفوفة من أزواج (timestamp، value) لسلسلة زمنية. ويمكن اشتقاق نوعَي عنصر timestamp والعنصر scalar في Tuple من تعريف INNER COLUMNS الخاص بـ samples (راجع تحديد الأعمدة الخارجية)
metric_family String اسم عائلة المقياس (للبيانات الوصفية للمقاييس)
type String نوع المقياس (مثل "counter" و"gauge")
unit String وحدة المقياس
help String وصف المقياس

مثال:

INSERT INTO my_table (metric_name, tags, time_series) VALUES
    ('cpu_usage', {'job': 'node_exporter', 'instance': 'host1:9100'},
     [(toDateTime64('2024-01-01 00:00:00', 3), 0.5), (toDateTime64('2024-01-01 00:01:00', 3), 0.7)])

يُسمح بأن تكون القيمة metric_name فارغة عند الإدخال، ما يعني أن اسم المقياس يُحدَّد في tags ضمن __name__، على سبيل المثال:

INSERT INTO my_table (tags, time_series) VALUES
    ({'__name__': 'cpu_usage', 'job': 'test'},
     [(toDateTime64('2024-01-01 00:00:00', 3), 0.5)])

لإدراج البيانات الوصفية للمقاييس، أدرِجها في الأعمدة metric_family وtype وunit وhelp:

INSERT INTO my_table (metric_name, tags, time_series, metric_family, type, unit, help) VALUES
    ('http_requests_total', {'method': 'GET'}, [(now64(), 100.0)],
     'http_requests_total', 'counter', 'requests', 'Total HTTP requests')

تحديد الأعمدة الخارجية

يمكن إدراج العمود الخارجي time_series صراحةً في عبارة CREATE TABLE لاستبدال نوعه الافتراضي Array(Tuple(DateTime64(3), Float64)). يستخرج ClickHouse نوعَي الطابع الزمني والقيمة scalar من الـ tuple ويُمرّرهما إلى جدول العينات الداخلي:

CREATE TABLE my_table (time_series Array(Tuple(UInt32, Float32))) ENGINE=TimeSeries

وهذا يعادل التصريح مباشرةً بأنواع أعمدة الطابع الزمني والقيمة في عبارة INNER COLUMNS الخاصة بجدول samples:

CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp UInt32 CODEC(DoubleDelta, ZSTD(1)), value Float32 CODEC(ZSTD(3)))

إذا استُخدمت الصيغتان كلتاهما ضمن عبارة CREATE TABLE نفسها، فيجب أن تتطابق الأنواع المُعلنة.

الجداول الهدف

لا يحتوي جدول TimeSeries على بيانات خاصة به، إذ يُخزَّن كل شيء في جداوله الهدف. وهذا يشبه طريقة عمل العرض المادي، مع فارق أن العرض المادي له جدول هدف واحد، بينما يحتوي جدول TimeSeries على ثلاثة جداول هدف باسم samples وtags وmetrics.

يمكن تحديد الجداول الهدف صراحةً في استعلام CREATE TABLE أو يمكن لمحرك جدول TimeSeries إنشاء الجداول الهدف الداخلية تلقائيًا.

تُحوَّل الصفوف المُدرجة في جدول TimeSeries، وتُقسَّم إلى كتل، ثم تُدرج في هذه الجداول الهدف الثلاثة.

الجداول الهدف هي كما يلي:

جدول العينات

يحتوي جدول samples على سلاسل زمنية مرتبطة بمعرّف معيّن.

يجب أن يحتوي جدول samples على الأعمدة التالية:

الاسم إلزامي؟ النوع الافتراضي الأنواع الممكنة الوصف
id [x] Tuple(UInt64, UUID) أيّ نوع يحدّد مجموعة من أسماء المقاييس والوسوم
timestamp [x] DateTime64(3) DateTime64(X) نقطة زمنية
value [x] Float64 Float32 أو Float64 قيمة مرتبطة بـ timestamp

تستخدم الأعمدة التي ينشئها المحرك تلقائيًا برامج ترميز ضغط للسلاسل الزمنية: timestamp CODEC(DoubleDelta, ZSTD(1)) وvalue CODEC(ZSTD(3)). بالكاد تُضغط الطوابع الزمنية شبه الرتيبة باستخدام برامج الترميز العامة، وقد تستحوذ لولا ذلك على معظم حجم جدول العينات على القرص. انظر أيضًا ضبط أنواع الأعمدة.

جدول الوسوم

يحتوي جدول tags على المعرّفات المحسوبة لكل توليفة من اسم مقياس والوسوم.

يجب أن يحتوي جدول tags على الأعمدة التالية:

Name Mandatory? Default type Possible types Description
id [x] Tuple(UInt64, UUID) any (must match the type of id in the samples table) يعرّف id توليفة من اسم مقياس والوسوم. ويحدّد تعبير DEFAULT كيفية حساب هذا المعرّف
metric_name [x] LowCardinality(String) String or LowCardinality(String) اسم المقياس
<tag_value_column> [ ] String String or LowCardinality(String) or LowCardinality(Nullable(String)) قيمة وسم معيّن، ويُحدَّد اسم الوسم واسم العمود المقابل له في الإعداد tags_to_columns
tags [x] Map(LowCardinality(String), String) Map(String, String) or Map(LowCardinality(String), String) or Map(LowCardinality(String), LowCardinality(String)) خريطة لجميع الوسوم، بما فيها الوسم __name__ الذي يحتوي على اسم المقياس، والوسوم التي أُدرجت أسماؤها في الإعداد tags_to_columns. كانت الجداول التي أنشأتها الإصدارات الأقدم من ClickHouse تخزّن في هذا العمود الوسوم التي لا تحتوي على أعمدة مخصصة فقط ودون اسم المقياس؛ وتدعم القراءة كلتا الحالتين
min_time [ ] Nullable(DateTime64(3)) DateTime64(X) or Nullable(DateTime64(X)) أدنى طابع زمني للسلسلة الزمنية ذات id هذا. يُنشأ العمود إذا كانت قيمة store_min_time_and_max_time هي true
max_time [ ] Nullable(DateTime64(3)) DateTime64(X) or Nullable(DateTime64(X)) أقصى طابع زمني للسلسلة الزمنية ذات id هذا. يُنشأ العمود إذا كانت قيمة store_min_time_and_max_time هي true

جدول المقاييس

يحتوي جدول metrics على بعض المعلومات حول المقاييس التي جُمعت، وأنواعها وأوصافها.

يجب أن يحتوي جدول metrics على الأعمدة التالية:

الاسم إلزامي؟ النوع الافتراضي الأنواع الممكنة الوصف
metric_family_name [x] String String أو LowCardinality(String) اسم عائلة المقياس
type [x] LowCardinality(String) String أو LowCardinality(String) نوع عائلة المقياس، ويكون إحدى القيم التالية: "counter" أو "gauge" أو "summary" أو "stateset" أو "histogram" أو "gaugehistogram"
unit [x] LowCardinality(String) String أو LowCardinality(String) الوحدة المستخدمة في المقياس
help [x] String String أو LowCardinality(String) وصف المقياس

الإنشاء

توجد عدة طرق لإنشاء جدول باستخدام محرك الجدول TimeSeries. أبسط عبارة

CREATE TABLE my_table ENGINE=TimeSeries

سينشئ فعليًا الجدول التالي (يمكنك التأكد من ذلك بتنفيذ SHOW CREATE TABLE my_table):

CREATE TABLE my_table
(
    `metric_name` String,
    `tags` Map(String, String),
    `time_series` Array(Tuple(DateTime64(3), Float64)),
    `metric_family` String,
    `type` String,
    `unit` String,
    `help` String
)
ENGINE = TimeSeries
SAMPLES INNER COLUMNS
(
    `id` Tuple(UInt64, UUID),
    `timestamp` DateTime64(3) CODEC(DoubleDelta, ZSTD(1)),
    `value` Float64 CODEC(ZSTD(3))
)
SAMPLES INNER ENGINE = MergeTree ORDER BY (id, timestamp) SETTINGS index_granularity = 32768
TAGS INNER COLUMNS
(
    `id` Tuple(UInt64, UUID) DEFAULT tuple(sipHash64(metric_name), reinterpretAsUUID(sipHash128(tags))),
    `metric_name` LowCardinality(String),
    `tags` Map(LowCardinality(String), String),
    `min_time` SimpleAggregateFunction(min, Nullable(DateTime64(3))),
    `max_time` SimpleAggregateFunction(max, Nullable(DateTime64(3)))
)
TAGS INNER ENGINE = AggregatingMergeTree PRIMARY KEY metric_name ORDER BY (metric_name, id) SETTINGS allow_dimensions_outside_sorting_key = 1, index_granularity = 8192
METRICS INNER COLUMNS
(
    `metric_family_name` String,
    `type` LowCardinality(String),
    `unit` LowCardinality(String),
    `help` String
)
METRICS INNER ENGINE = ReplacingMergeTree ORDER BY metric_family_name

لذا جرى إنشاء الأعمدة تلقائيًا، وهناك أيضًا ثلاثة جداول هدف داخلية لها تعريفات أعمدة خاصة بها مخزّنة في عبارة INNER COLUMNS.

تحمل الجداول الهدف الداخلية أسماء مثل .inner_id.samples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, .inner_id.tags.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx، و.inner_id.metrics.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ولكل جدول هدف مجموعة الأعمدة الخاصة به:

CREATE TABLE default.`.inner_id.samples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, UUID),
    `timestamp` DateTime64(3) CODEC(DoubleDelta, ZSTD(1)),
    `value` Float64 CODEC(ZSTD(3))
)
ENGINE = MergeTree
ORDER BY (id, timestamp)
SETTINGS index_granularity = 32768
CREATE TABLE default.`.inner_id.tags.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, UUID) DEFAULT tuple(sipHash64(metric_name), reinterpretAsUUID(sipHash128(tags))),
    `metric_name` LowCardinality(String),
    `tags` Map(LowCardinality(String), String),
    `min_time` SimpleAggregateFunction(min, Nullable(DateTime64(3))),
    `max_time` SimpleAggregateFunction(max, Nullable(DateTime64(3)))
)
ENGINE = AggregatingMergeTree
PRIMARY KEY metric_name
ORDER BY (metric_name, id)
SETTINGS allow_dimensions_outside_sorting_key = 1, index_granularity = 8192
CREATE TABLE default.`.inner_id.metrics.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `metric_family_name` String,
    `type` LowCardinality(String),
    `unit` LowCardinality(String),
    `help` String
)
ENGINE = ReplacingMergeTree
ORDER BY metric_family_name
SETTINGS index_granularity = 8192

إنشاء جدول AS من جدول موجود

تنسخ العبارة CREATE TABLE new_table AS existing_table من existing_table ما يلي:

  • SETTINGS
  • INNER COLUMNS لكل نوع
  • INNER ENGINE لكل نوع

لا يُسمح بهذه العبارة إذا كان existing_table يحتوي على أهداف خارجية. يُعاد توليد قائمة الأعمدة الخارجية ولا تُنسخ.

ضبط أنواع الأعمدة

يمكنك ضبط أنواع الأعمدة في الجداول الهدف الداخلية باستخدام عبارة INNER COLUMNS. على سبيل المثال، لتخزين الطوابع الزمنية بالميكروثانية والقيم كـ Float32، استخدم:

CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6) CODEC(DoubleDelta, ZSTD(1)), value Float32 CODEC(ZSTD(3)))

يعني تحديد الأعمدة الداخلية دون برامج ترميز الضغط استخدام برنامج ترميز الضغط الافتراضي لها:

CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6), value Float32)

العمود id

يحتوي العمود id على معرّفات، ويُحتسب كل معرّف استنادًا إلى تركيبة من اسم المقياس والوسوم. يمكن تخصيص النوع وتعبير DEFAULT المستخدم لتوليد المعرّفات عبر العبارة TAGS INNER COLUMNS:

CREATE TABLE my_table ENGINE=TimeSeries
TAGS INNER COLUMNS (id UInt64 DEFAULT sipHash64(tags))

يمكن أن يكون العمود id من أي نوع غير Nullable قابل للمقارنة. يجب أن تتطابق أنواع id المُعلنة في الجدولين الداخليين samples وtags.

إذا لم يتم تحديد التعبير DEFAULT للعمود id ولم يكن الإعداد id_generator مُعيَّنًا، فسيختار ClickHouse التعبير DEFAULT تلقائيًا بناءً على نوع id، ولكن فقط إذا كان نوع id أحد الأنواع UUID أو UInt64 أو UInt128 أو FixedString(16)، أو زوجًا من اثنين من هذه الأنواع. بالنسبة إلى هذا الزوج، يحسب التعبير المختار تلقائيًا تجزئةً لاسم المقياس في المكوّن الأول وتجزئةً لجميع الوسوم في المكوّن الثاني.

يوفّر الإعداد id_generator مستوى التخصيص نفسه من دون استخدام العبارة INNER COLUMNS:

CREATE TABLE my_table ENGINE=TimeSeries
SETTINGS id_generator = 'sipHash64(tags)'

إذا كان هذا الإعداد مُعيَّنًا، فسيُستخدم لتوليد id حتى إذا كانت قيمة DEFAULT الخاصة بالعمود تحتوي على تعبير مختلف.

العمود tags

يحتوي العمود tags على جميع وسوم السلسلة الزمنية، بما في ذلك الوسم __name__ الذي يحتوي على اسم المقياس.

يتيح الإعداد tags_to_columns تحديد وسم معيّن لتخزينه أيضًا في عمود منفصل بالإضافة إلى الخريطة داخل العمود tags:

CREATE TABLE my_table
ENGINE = TimeSeries
SETTINGS tags_to_columns = {'instance': 'instance', 'job': 'job'}

ستضيف هذه العبارة العمودين instance وjob إلى جدول الوسوم الهدف الداخلي. ستُخزَّن قيم الوسمين instance وjob في العمودين المذكورين وفي العمود tags.

محركات الجداول الخاصة بالجداول الهدف الداخلية

تستخدم الجداول الهدف الداخلية، افتراضيًا، محركات الجداول التالية:

  • يستخدم جدول العينات محرك MergeTree;
  • يستخدم جدول الوسوم محرك AggregatingMergeTree لأن البيانات نفسها تُدرج في هذا الجدول غالبًا عدة مرات، لذا نحتاج إلى طريقة لإزالة التكرارات، وكذلك لأن التجميع مطلوب للعمودين min_time و max_time;
  • يستخدم جدول المقاييس محرك ReplacingMergeTree لأن البيانات نفسها تُدرج في هذا الجدول غالبًا عدة مرات، لذا نحتاج إلى طريقة لإزالة التكرارات.

كما يمكن استخدام محركات جداول أخرى للجداول الهدف الداخلية إذا تم تحديد ذلك:

CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES ENGINE=ReplicatedMergeTree
TAGS ENGINE=ReplicatedAggregatingMergeTree
METRICS ENGINE=ReplicatedReplacingMergeTree

يُبقي جدول الوسوم أعمدة الوسوم (والـ Map tags) خارج مفتاح الفرز الخاص به، وهو ما يرفضه AggregatingMergeTree افتراضيًا (راجع allow_dimensions_outside_sorting_key). وهذا آمن هنا لأن تلك الأعمدة تعتمد وظيفيًا على id، وهو جزء من مفتاح الفرز، لذا فإن جميع الصفوف التي تدمجها عملية دمج في الخلفية معًا تتشارك القيم نفسها. وعندما يُنشأ جدول الوسوم الداخلي أو يُحدَّد محركه inline كما هو موضح أعلاه، يضبط TimeSeries القيمة allow_dimensions_outside_sorting_key = 1 عليه تلقائيًا؛ أما بالنسبة إلى جدول الوسوم التجميعي الخارجي الذي يُنشأ يدويًا، فيجب عليك تعيين هذا الإعداد بنفسك.

الجداول الهدف الخارجية

يمكن إعداد جدول TimeSeries لاستخدام جدول أُنشئ يدويًا:

CREATE TABLE samples_for_my_table
(
    `id` UUID,
    `timestamp` DateTime64(3),
    `value` Float64
)
ENGINE = MergeTree
ORDER BY (id, timestamp);

CREATE TABLE tags_for_my_table ...

CREATE TABLE metrics_for_my_table ...

CREATE TABLE my_table ENGINE=TimeSeries SAMPLES samples_for_my_table TAGS tags_for_my_table METRICS metrics_for_my_table;

يجب أن تتطابق أنواع أعمدة الجداول الخارجية (id وtimestamp وvalue وأعمدة <tag_value_column> المدرجة في tags_to_columns) مع ما كان جدول TimeSeries سيُنشئه داخليًا في الحالة العادية (راجع جدول العينات وجدول الوسوم وجدول المقاييس للاطلاع على قيود الأنواع). ويُبلَّغ عن أي عدم تطابق في الأنواع عند تنفيذ CREATE.

يُحدَّد تعبير مولِّد المعرّف لهدف وسوم خارجي عند وقت INSERT بالترتيب التالي: الإعداد id_generator (إذا كان معيَّنًا)، ثم DEFAULT المعرَّف في عمود id للجدول الخارجي (إن وجد)، ثم المولِّد القياسي المشتق من نوع id. لذلك، يتجاوز هذا الإعداد أي قيمة DEFAULT معرَّفة في الجدول الخارجي — راجع عمود id للتفاصيل.

تعديل الإعدادات

يمكن تغيير إعدادَين بعد CREATE:

  • id_generator
  • filter_by_min_time_and_max_time
ALTER TABLE my_table MODIFY SETTING id_generator = 'sipHash64(tags)';
ALTER TABLE my_table MODIFY SETTING filter_by_min_time_and_max_time = 0;

لاحظ أن تغيير id_generator بعد وجود بيانات بالفعل في جدول Tags قد يؤدي إلى إنشاء معرّفات مختلفة لنفس تركيبة metric+tag — إذ تحتفظ الصفوف القديمة بمعرّفاتها القديمة، بينما تستخدم الصفوف الجديدة المولِّد الجديد.

ولا يمكن تغيير الإعدادات الأخرى باستخدام ALTER ... MODIFY SETTING لأنها تكون مدمجة في مخطط الجداول الداخلية عند CREATE.

الإعدادات

فيما يلي قائمة بالإعدادات التي يمكن تحديدها عند تعريف جدول TimeSeries:

الاسم النوع الافتراضي الوصف
id_generator Expression يعتمد على نوع id تعبير يحسب معرّف (بصمة) السلسلة الزمنية من وسومها. إذا لم يتم تعيينه، فسيُستخدم التعبير الافتراضي للعمود id. وإذا كان التعبير الافتراضي للعمود id غير معيّن أيضًا، فسيُختار التعبير تلقائيًا
tags_to_columns خريطة قيمة خريطة تحدد الوسوم التي يجب وضعها في أعمدة منفصلة في جدول وسم. الصياغة: {'tag1': 'column1', 'tag2' : column2, ...}
use_all_tags_column_to_generate_id Bool false إعداد متقادم، لا يفعل شيئًا
store_min_time_and_max_time Bool true إذا تم تعيينه إلى true، فسيخزّن الجدول min_time وmax_time لكل سلسلة زمنية
aggregate_min_time_and_max_time Bool true عند إنشاء جدول tags الداخلي الهدف، يتيح هذا الخيار استخدام SimpleAggregateFunction(min, Nullable(DateTime64(3))) بدلًا من Nullable(DateTime64(3)) فقط كنوع للعمود min_time، وينطبق الأمر نفسه على العمود max_time
filter_by_min_time_and_max_time Bool true إذا تم تعيينه إلى true، فسيستخدم الجدول العمودين min_time وmax_time لتصفية السلاسل الزمنية
samples_index_granularity UInt64 32768 يضبط index_granularity لجدول عينة الداخلي. عند تعيينه صراحةً، فإنه يتجاوز index_granularity من تعريف المحرك. يُتجاهل لجدول عينة خارجي ولمحرك غير MergeTree
tags_index_granularity UInt64 8192 يضبط index_granularity لجدول وسم الداخلي. عند تعيينه صراحةً، فإنه يتجاوز index_granularity من تعريف المحرك. يُتجاهل لجدول وسم خارجي ولمحرك غير MergeTree
recent_samples_ttl_seconds UInt64 345600 مدة الاحتفاظ بجدول الهدف الإضافي recent samples، الذي تُكتب إليه أيضًا كل عينة مُدرجة. يحصل جدول العينات الحديثة الداخلي دائمًا على TTL toDateTime(timestamp) + toIntervalSecond(recent_samples_ttl_seconds) مشتق من هذا الإعداد (متجاوزًا أي TTL من تعريف المحرك)؛ ويجب أن يحتفظ جدول العينات الحديثة الخارجي ببيانات لهذه المدة من الثواني على الأقل. تفضّل الاستعلامات التي يقع نطاقها الزمني ضمن نافذة TTL جدول العينات الحديثة على جدول العينات الرئيسي (راجع الإعداد على مستوى الاستعلام time_series_prefer_recent_samples_table). القيمة الافتراضية هي 4 أيام؛ وتُثبّت القيمة الفعلية في تعريف الجدول وقت CREATE. عيّنه إلى 0 لتعطيل جدول العينات الحديثة
recent_samples_partition_by Expression toStartOfInterval(toDateTime(timestamp), toIntervalHour(5)) مفتاح التقسيم لجدول recent samples الداخلي، مثلًا toStartOfHour(timestamp). يتطلب أن تكون recent_samples_ttl_seconds غير صفرية
recent_samples_index_granularity UInt64 8192 يضبط index_granularity لجدول recent samples الداخلي. يتطلب أن تكون recent_samples_ttl_seconds غير صفرية

الدوال

فيما يلي قائمة بالدوال التي تقبل جدول TimeSeries كوسيطة:

Navigation