Skip to main content
Движок MergeTree и другие движки семейства MergeTree (например, ReplacingMergeTree, AggregatingMergeTree) — наиболее широко используемые и самые надёжные движки таблиц в ClickHouse. Движки таблиц семейства MergeTree рассчитаны на высокую скорость приёма данных и очень большие объёмы данных. Операции вставки создают части таблицы, которые затем фоновый процесс объединяет с другими частями таблицы. Основные возможности движков таблиц семейства MergeTree.
  • Первичный ключ таблицы определяет порядок сортировки внутри каждой части таблицы (кластерный индекс). При этом первичный ключ ссылается не на отдельные строки, а на блоки по 8192 строк, называемые гранулами. Благодаря этому первичные ключи даже для очень больших наборов данных остаются достаточно компактными, чтобы помещаться в оперативной памяти, и при этом обеспечивают быстрый доступ к данным на диске.
  • Таблицы можно разбивать на партиции с помощью произвольного выражения партиционирования. Отсечение партиций позволяет не читать партиции, если это допускает запрос.
  • Данные могут реплицироваться между несколькими узлами кластера для высокой доступности, переключения при сбоях и обновлений без простоя. См. Репликация данных.
  • Движки таблиц MergeTree поддерживают различные виды статистики и методы сэмплирования, помогающие оптимизации запросов.
Несмотря на похожее название, движок Merge отличается от движков *MergeTree.

Создание таблиц

Подробное описание параметров см. в описании оператора CREATE TABLE

Секции запроса

ENGINE

ENGINE — имя и параметры движка. ENGINE = MergeTree(). У движка MergeTree нет параметров.

ORDER BY

ORDER BY — ключ сортировки. Кортеж из имён столбцов или произвольных выражений. Пример: ORDER BY (CounterID + 1, EventDate). Если первичный ключ не определён (то есть PRIMARY KEY не был указан), ClickHouse использует ключ сортировки в качестве первичного ключа. Если сортировка не нужна, можно использовать синтаксис ORDER BY tuple(). Либо, если настройка create_table_empty_primary_key_by_default включена, в команды CREATE TABLE неявно добавляется ORDER BY (). См. Выбор первичного ключа.

PARTITION BY

PARTITION BYключ партиционирования. Необязателен. В большинстве случаев ключ партиционирования не нужен, а если партиционирование всё же требуется, то, как правило, ключ партиционирования с детализацией мельче месяца тоже не нужен. Партиционирование не ускоряет запросы (в отличие от выражения ORDER BY). Никогда не используйте слишком мелкое партиционирование. Не разбивайте данные на партиции по идентификаторам или именам клиентов (вместо этого сделайте идентификатор или имя клиента первым столбцом в выражении ORDER BY). Для партиционирования по месяцам используйте выражение toYYYYMM(date_column), где date_column — столбец с датой типа Date. Имена партиций здесь имеют формат "YYYYMM".

PRIMARY KEY

PRIMARY KEY — первичный ключ, если он отличается от ключа сортировки. Необязателен. При указании ключа сортировки (с помощью предложения ORDER BY) первичный ключ задаётся неявно. Обычно отдельно указывать первичный ключ помимо ключа сортировки не требуется.

SAMPLE BY

SAMPLE BY — выражение для семплирования. Необязательный параметр. Если оно указано, то должно входить в первичный ключ. Выражение для семплирования должно возвращать беззнаковое целое число. Пример: SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID)).

TTL

TTL — список правил, задающих срок хранения строк и логику автоматического перемещения частей между дисками и томами. Необязательный параметр. Выражение должно возвращать Date или DateTime, например TTL date + INTERVAL 1 DAY. Тип правила DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'|GROUP BY задаёт действие, которое должно быть выполнено с частью, если условие выражения выполнено (то есть достигнуто текущее время): удаление устаревших строк, перемещение части (если выражение выполнено для всех строк в части) на указанный диск (TO DISK 'xxx') или в указанный том (TO VOLUME 'xxx'), либо агрегирование значений в устаревших строках. Тип правила по умолчанию — удаление (DELETE). Можно задать список из нескольких правил, но правило DELETE должно быть только одно. Подробнее см. в разделе TTL для столбцов и таблиц

НАСТРОЙКИ

См. настройки MergeTree. Пример для настройки Sections
В примере мы настраиваем партиционирование по месяцам. Мы также задаём выражение для семплирования в виде хеша по идентификатору пользователя. Это позволяет псевдослучайно распределить данные в таблице для каждого CounterID и EventDate. Если при выборке данных указать предложение SAMPLE, ClickHouse вернёт равномерную псевдослучайную выборку данных для подмножества пользователей. Настройку index_granularity можно опустить, поскольку 8192 — значение по умолчанию.

Хранение данных

Таблица состоит из частей данных, отсортированных по первичному ключу. При вставке данных в таблицу создаются отдельные части данных, и каждая из них лексикографически упорядочивается по первичному ключу. Например, если первичный ключ — (CounterID, Date), данные в части сортируются по CounterID, а внутри каждого CounterID — по Date. Данные, относящиеся к разным партициям, разделяются на разные части. В фоновом режиме ClickHouse выполняет слияние частей данных для более эффективного хранения. Части, относящиеся к разным партициям, не сливаются. Механизм слияния не гарантирует, что все строки с одинаковым первичным ключом окажутся в одной и той же части данных. Части данных могут храниться в формате Wide или Compact. В формате Wide каждый столбец хранится в отдельном файле в файловой системе, а в формате Compact все столбцы хранятся в одном файле. Формат Compact можно использовать для повышения производительности небольших и частых вставок. Формат хранения данных задаётся настройками min_bytes_for_wide_part и min_rows_for_wide_part движка таблицы. Если количество байт или строк в части данных меньше значения соответствующей настройки, часть хранится в формате Compact. В противном случае она хранится в формате Wide. Если ни одна из этих настроек не задана, части данных хранятся в формате Wide. Каждая часть данных логически делится на гранулы. Гранула — это наименьший неделимый набор данных, который ClickHouse считывает при выборке. ClickHouse не разделяет строки или значения, поэтому каждая гранула всегда содержит целое число строк. Первая строка гранулы помечается значением первичного ключа этой строки. Для каждой части данных ClickHouse создаёт индексный файл, в котором хранятся метки. Для каждого столбца, независимо от того, входит он в первичный ключ или нет, ClickHouse также сохраняет те же метки. Эти метки позволяют находить данные напрямую в файлах столбцов. Размер гранулы ограничивается настройками index_granularity и index_granularity_bytes движка таблицы. Число строк в грануле находится в диапазоне [1, index_granularity] и зависит от размера строк. Размер гранулы может превышать index_granularity_bytes, если размер одной строки больше значения этой настройки. В этом случае размер гранулы равен размеру строки.

Первичные ключи и индексы в запросах

Рассмотрим в качестве примера первичный ключ (CounterID, Date). В этом случае сортировку и индекс можно представить следующим образом:
Если запрос к данным указывает:
  • CounterID in ('a', 'h'), сервер читает данные в диапазонах меток [0, 3) и [6, 8).
  • CounterID IN ('a', 'h') AND Date = 3, сервер читает данные в диапазонах меток [1, 3) и [7, 8).
  • Date = 3, сервер читает данные в диапазоне меток [1, 10].
Приведённые выше примеры показывают, что использовать индекс всегда эффективнее, чем выполнять полное сканирование. Разреженный индекс допускает чтение дополнительных данных. При чтении одного диапазона первичного ключа в каждом блоке данных может быть прочитано до index_granularity * 2 дополнительных строк. Разреженные индексы позволяют работать с очень большим количеством строк таблицы, потому что в большинстве случаев такие индексы помещаются в оперативную память компьютера. ClickHouse не требует уникального первичного ключа. Вы можете вставлять несколько строк с одинаковым первичным ключом. Вы можете использовать выражения типа Nullable в секциях PRIMARY KEY и ORDER BY, но делать это настоятельно не рекомендуется. Чтобы включить эту возможность, включите настройку allow_nullable_key. Для значений NULL в секции ORDER BY применяется принцип NULLS_LAST.

Выбор первичного ключа

Количество столбцов в первичном ключе явно не ограничено. В зависимости от структуры данных вы можете включить в первичный ключ больше или меньше столбцов. Это может:
  • Повысить производительность индекса. Если первичный ключ — (a, b), то добавление ещё одного столбца c повысит производительность, если выполняются следующие условия:
    • Есть запросы с условием по столбцу c.
    • Часто встречаются длинные диапазоны данных (в несколько раз длиннее, чем index_granularity) с одинаковыми значениями (a, b). Иными словами, если добавление ещё одного столбца позволяет пропускать достаточно длинные диапазоны данных.
  • Улучшить сжатие данных. ClickHouse сортирует данные по первичному ключу, поэтому чем выше упорядоченность данных, тем лучше сжатие.
  • Обеспечить дополнительную логику при слиянии частей данных в движках CollapsingMergeTree и SummingMergeTree. В этом случае имеет смысл указать ключ сортировки, который отличается от первичного ключа.
Длинный первичный ключ отрицательно скажется на производительности вставки и потреблении памяти, но дополнительные столбцы в первичном ключе не влияют на производительность ClickHouse при выполнении запросов SELECT. Вы можете создать таблицу без первичного ключа, используя синтаксис ORDER BY tuple(). В этом случае ClickHouse хранит данные в порядке их вставки. Если вы хотите сохранить порядок данных при вставке с помощью запросов INSERT ... SELECT, установите max_insert_threads = 1. Чтобы выбирать данные в исходном порядке, используйте однопоточные запросы SELECT.

Выбор первичного ключа, отличающегося от ключа сортировки

Можно указать первичный ключ (выражение со значениями, которые записываются в индексный файл для каждой метки), отличный от ключа сортировки (выражения для сортировки строк в частях данных). В этом случае кортеж выражения первичного ключа должен быть префиксом кортежа выражения ключа сортировки. Эта возможность полезна при использовании движков таблиц SummingMergeTree и AggregatingMergeTree. В типичном случае при использовании этих движков таблица содержит два типа столбцов: измерения и меры. Обычно запросы агрегируют значения столбцов-мер с произвольным GROUP BY и фильтрацией по измерениям. Поскольку SummingMergeTree и AggregatingMergeTree агрегируют строки с одинаковым значением ключа сортировки, логично включить в него все измерения. В результате выражение ключа представляет собой длинный список столбцов, и этот список приходится часто обновлять по мере добавления новых измерений. В этом случае имеет смысл оставить в первичном ключе только несколько столбцов, которые обеспечат эффективное сканирование диапазонов, а остальные столбцы измерений добавить в кортеж ключа сортировки. ALTER ключа сортировки — лёгкая операция, потому что, когда новый столбец одновременно добавляется в таблицу и в ключ сортировки, существующие части данных не нужно изменять. Поскольку старый ключ сортировки является префиксом нового ключа сортировки и в только что добавленном столбце ещё нет данных, в момент изменения таблицы данные уже отсортированы и по старому, и по новому ключу сортировки.

Использование индексов и партиций в запросах

Для запросов SELECT ClickHouse анализирует, можно ли использовать индекс. Индекс может быть использован, если в условии WHERE/PREWHERE есть выражение (либо как один из элементов конъюнкции, либо целиком), представляющее собой операцию сравнения на равенство или неравенство, либо если в нём есть IN или LIKE с фиксированным префиксом для столбцов или выражений, входящих в первичный ключ или ключ партиционирования, а также для некоторых частично повторяющихся функций от этих столбцов или логических связей этих выражений. Таким образом, можно быстро выполнять запросы по одному или нескольким диапазонам первичного ключа. В этом примере запросы будут выполняться быстро для конкретного тега отслеживания, для конкретного тега и диапазона дат, для конкретного тега и даты, для нескольких тегов в диапазоне дат и так далее. Давайте рассмотрим движок, настроенный следующим образом:
В таком случае в запросах:
ClickHouse будет использовать индекс первичного ключа, чтобы отсечь неподходящие данные, и ключ партиционирования по месяцам — чтобы отсечь партиции, выходящие за пределы нужных диапазонов дат. Приведенные выше запросы показывают, что индекс используется даже для сложных выражений. Чтение из таблицы организовано так, что использование индекса не может быть медленнее полного сканирования. В примере ниже индекс использовать нельзя.
Чтобы проверить, может ли ClickHouse использовать индекс при выполнении запроса, воспользуйтесь настройками force_index_by_date и force_primary_key. Ключ партиционирования по месяцам позволяет считывать только те блоки данных, которые содержат даты из нужного диапазона. В этом случае блок данных может содержать данные для множества дат (вплоть до целого месяца). Внутри блока данные сортируются по первичному ключу, и дата может не быть в нём первым столбцом. Поэтому запрос только с условием по дате, без указания префикса первичного ключа, приведёт к чтению большего объёма данных, чем в случае с одной датой.

Использование индекса для детерминированных выражений в первичных ключах

Первичный ключ может содержать не только имена столбцов, но и выражения. Эти выражения не ограничиваются простыми цепочками функций: это могут быть произвольные деревья выражений (например, вложенные функции и составные выражения), если они детерминированы. Выражение считается детерминированным, если оно всегда возвращает один и тот же результат для одних и тех же входных значений (например: length(), toDate(), lower(), left(), cityHash64(), toUUID(); в отличие от now() или rand()). Если первичный ключ содержит детерминированные выражения, ClickHouse может применить их к константным значениям из запроса и использовать результат для построения условий по индексу первичного ключа. Это позволяет пропускать данные для таких предикатов, как =, IN и has. Распространённый сценарий — сделать первичный ключ компактным (например, хранить хеш вместо длинной String), при этом сохранив возможность использовать индекс для предикатов по исходному столбцу. Пример детерминированного (но не инъективного) первичного ключа:
Примеры предикатов, для которых может использоваться индекс:
В этих случаях ClickHouse вычисляет length('alice') (и другие константы) один раз и использует значения длины, чтобы сузить диапазоны в индексе первичного ключа. Поскольку длина строки неинъективна, разные строки user_id могут иметь одинаковую длину, поэтому индекс может считывать лишние гранулы (ложноположительные срабатывания). При этом результат остаётся корректным, поскольку после чтения всё равно применяется исходный предикат (user_id = ..., IN и т. д.). Если детерминированное выражение также инъективно (разные входные данные не могут давать один и тот же результат для используемых типов аргументов), ClickHouse также может эффективно использовать индекс для отрицательных форм: !=, NOT IN и NOT has(...). Например, reverse(p) и hex(p) инъективны для String. Пример инъективного первичного ключа:
Поддерживаются и более сложные инъективные выражения, например:
Примеры предикатов, для которых можно использовать индекс:

Использование индекса для частично-монотонных первичных ключей

Рассмотрим, например, дни месяца. В пределах одного месяца они образуют монотонную последовательность, но на более длительных промежутках уже не являются монотонными. Это частично-монотонная последовательность. Если пользователь создает таблицу с частично-монотонным первичным ключом, ClickHouse, как обычно, создает разреженный индекс. При выборке данных из такой таблицы ClickHouse анализирует условия запроса. Если нужно получить данные между двумя метками индекса и обе эти метки находятся в пределах одного месяца, ClickHouse может использовать индекс в этом конкретном случае, поскольку может вычислить расстояние между параметрами запроса и метками индекса. ClickHouse не может использовать индекс, если значения первичного ключа в диапазоне параметров запроса не образуют монотонную последовательность. В этом случае ClickHouse выполняет полное сканирование. ClickHouse применяет эту логику не только к последовательностям дней месяца, но и к любому первичному ключу, представляющему собой частично-монотонную последовательность.

Индексы пропуска данных

Объявление индекса находится в разделе столбцов запроса CREATE.
Для таблиц семейства *MergeTree можно задавать индексы пропуска данных. Эти индексы агрегируют информацию о заданном выражении по блокам, состоящим из granularity_value гранул (размер гранулы задаётся настройкой index_granularity в движке таблицы). Затем эти агрегаты используются в запросах SELECT, чтобы уменьшить объём данных, считываемых с диска, пропуская большие блоки данных, для которых условие where не может быть выполнено. Предложение GRANULARITY можно опустить; значение granularity_value по умолчанию равно 1. Пример
ClickHouse может использовать индексы из примера, чтобы сократить объём данных, считываемых с диска, в следующих запросах:
Индексы пропуска данных также можно создавать по составным столбцам:

Типы индексов пропуска данных

Движок таблицы MergeTree поддерживает следующие типы индексов пропуска данных. Дополнительные сведения об использовании индексов пропуска данных для оптимизации производительности см. в статье “Принципы работы индексов пропуска данных в ClickHouse”.

Индекс пропуска данных MinMax

Для каждой гранулы индекса хранятся минимальное и максимальное значения выражения. (Если выражение имеет тип tuple, для каждого элемента кортежа хранятся минимальное и максимальное значения.)
Syntax

Set

Для каждой гранулы индекса хранится не более max_rows уникальных значений указанного выражения. max_rows = 0 означает “хранить все уникальные значения”.
Syntax

Bloom-фильтр

Для каждой гранулы индекса хранится bloom-фильтр для указанных столбцов.
Syntax
Параметр false_positive_rate может принимать значение от 0 до 1 (по умолчанию — 0.025) и задаёт вероятность ложноположительного срабатывания (что увеличивает объём читаемых данных). Поддерживаются следующие типы данных:
  • (U)Int*
  • Float*
  • Enum
  • Date
  • DateTime
  • String
  • FixedString
  • Array
  • LowCardinality
  • Nullable
  • UUID
  • Map
Тип данных Map: создание индекса по ключам или значениямДля типа данных Map client может указать, должен ли индекс создаваться по ключам или по значениям, с помощью функций mapKeys или mapValues.
Тип данных JSON: индексация JSON-путейДля типа данных JSON можно создать bloom-фильтр индекс по набору путей с помощью функции JSONAllPaths. Это позволяет пропускать гранулы, в которых отсутствует запрашиваемый JSON-путь. Подробности см. в разделе Индексы пропуска данных для JSON.

N-граммный bloom-фильтр (Устарело)

Начиная с ClickHouse 26.2, когда индекс text получил статус General Availability (GA), индекс ngrambf_v1 больше не рекомендуется использовать для полнотекстового поиска.Подробности см. на странице “Полнотекстовый поиск с помощью текстовых индексов”.
Для каждой гранулы индекса хранится bloom-фильтр для n-грамм указанных столбцов.
Syntax
Этот индекс работает только со следующими типами данных: Чтобы оценить параметры ngrambf_v1, можно использовать следующие пользовательские функции (UDF).
UDFs for ngrambf_v1
Чтобы использовать эти функции, необходимо указать как минимум два параметра:
  • total_number_of_all_grams
  • probability_of_false_positives
Например, в грануле содержится 4300 n-грамм, и вы ожидаете, что вероятность ложноположительных срабатываний будет меньше 0.0001. Остальные параметры затем можно оценить, выполнив следующие запросы:
Конечно, вы также можете использовать эти функции, чтобы оценить параметры для других случаев. Приведённые выше функции отсылают к калькулятору bloom-фильтра здесь.

Токенный bloom-фильтр

Начиная с версии ClickHouse 26.2, в которой индекс text стал общедоступным (GA), индекс tokenbf_v1 больше не рекомендуется для полнотекстового поиска.Подробнее см. на странице “Полнотекстовый поиск с помощью текстовых индексов”.
Syntax

Bloom-фильтр для sparse grams

Bloom-фильтр для sparse grams похож на ngrambf_v1, но использует токены sparse grams вместо n-грамм.
Syntax

Текстовый индекс

Создаёт инвертированный индекс по токенизированным строковым данным, обеспечивая эффективный и детерминированный полнотекстовый поиск. Подробности см. здесь.

Векторное сходство

Поддерживается приближенный поиск ближайших соседей; подробности см. здесь.

Поддержка функций

Условия в предложении WHERE содержат вызовы функций, работающих со столбцами. Если столбец входит в индекс, ClickHouse пытается использовать этот индекс при выполнении этих функций. ClickHouse поддерживает разные подмножества функций для использования индексов. Индексы типа set могут использоваться всеми функциями. Другие типы индексов поддерживаются следующим образом: Функции с константным аргументом, который меньше размера n-граммы, не могут использоваться ngrambf_v1 для оптимизации запросов. (*) Чтобы hasTokenCaseInsensitive и hasTokenCaseInsensitiveOrNull работали эффективно, индекс tokenbf_v1 должен быть создан по данным, приведённым к нижнему регистру, например INDEX idx (lower(str_col)) TYPE tokenbf_v1(512, 3, 0).
Bloom-фильтры могут давать ложноположительные срабатывания, поэтому индексы ngrambf_v1, tokenbf_v1, sparse_grams и bloom_filter не могут использоваться для оптимизации запросов, в которых ожидается, что результат функции будет ложным.Например:
  • Могут быть оптимизированы:
    • s LIKE '%test%'
    • NOT s NOT LIKE '%test%'
    • s = 1
    • NOT s != 1
    • startsWith(s, 'test')
  • Не могут быть оптимизированы:
    • NOT s LIKE '%test%'
    • s NOT LIKE '%test%'
    • NOT s = 1
    • s != 1
    • NOT startsWith(s, 'test')

Проекции

Проекции похожи на materialized views, но определяются на уровне частей данных. Они обеспечивают гарантии согласованности и автоматически используются в запросах.
При использовании проекций следует также учитывать настройку force_optimize_projection.
Проекции не поддерживаются в запросах SELECT с модификатором FINAL.

Запрос проекции

Запрос проекции определяет проекцию. Он неявно выбирает данные из родительской таблицы. Синтаксис
Проекции можно изменять или удалять с помощью оператора ALTER.

Индексы проекций

Индексы проекций расширяют подсистему проекций, предоставляя лёгкий и явный способ определять индексы на уровне проекций. Внешне индекс проекции по-прежнему остаётся проекцией, но с упрощённым синтаксисом и более ясным назначением: он определяет выражение, предназначенное для фильтрации, а не для хранения материализованных данных. Внутри индекс проекции не материализует исходную таблицу в переставленном порядке строк, как обычная проекция. Вместо этого перестановка хранится в виде числового столбца _part_offset, то есть SELECT _part_offset ORDER BY <index_expr>.

Синтаксис

Пример:

Типы индексов

В настоящее время поддерживаются:
  • basic: эквивалентен обычному индексу MergeTree по выражению.
В будущем этот механизм позволит добавлять новые типы индексов.

Хранение проекций

Проекции хранятся внутри каталога части. Это похоже на индекс, но здесь есть подкаталог, в котором хранится часть анонимной таблицы MergeTree. Эта таблица создается на основе запроса, задающего определение проекции. Если есть секция GROUP BY, нижележащий движок хранения становится AggregatingMergeTree, а все агрегатные функции преобразуются в AggregateFunction. Если есть секция ORDER BY, таблица MergeTree использует ее как выражение первичного ключа. При слиянии часть проекции объединяется с помощью процедуры слияния соответствующего хранилища. Контрольная сумма части родительской таблицы объединяется с контрольной суммой части проекции. Остальные задачи обслуживания аналогичны индексам пропуска.

Анализ запроса

  1. Проверьте, можно ли использовать проекцию для выполнения данного запроса, то есть даст ли она тот же результат, что и запрос к базовой таблице.
  2. Выберите наилучший подходящий вариант, для которого требуется прочитать наименьшее число гранул.
  3. Конвейер запроса, использующий проекции, будет отличаться от того, который использует исходные части. Если в некоторых частях проекция отсутствует, можно добавить конвейер, который построит её на лету.

Одновременный доступ к данным

Для одновременного доступа к таблице используется многоверсионность. Иными словами, когда таблицу одновременно читают и обновляют, данные читаются из набора частей, актуального на момент выполнения запроса. Длительные блокировки отсутствуют. Операции вставки не мешают чтению. Чтение из таблицы автоматически распараллеливается.

TTL для столбцов и таблиц

Определяет срок хранения значений. Клаузу TTL можно задать для всей таблицы и для каждого отдельного столбца. TTL на уровне таблицы также может задавать логику автоматического перемещения данных между дисками и томами или повторного сжатия частей, для которых срок хранения всех данных уже истёк. Выражения должны иметь тип данных Date, Date32, DateTime или DateTime64.
Избегайте недетерминированных функций в TTL-выраженияхTTL вычисляется во время фоновых слияний, а не в момент вставки. Такие функции, как rand(), now() или now64(), будут вычисляться заново при каждом слиянии, что приведёт к непредсказуемому поведению при удалении. ClickHouse блокирует выражения, которые вообще не зависят от столбцов, но в настоящее время не отклоняет недетерминированные функции, используемые вместе со ссылкой на столбец (например, ts + rand()). Для предсказуемых результатов TTL-выражения должны основываться исключительно на детерминированных значениях, производных от столбцов.
Синтаксис Задание time-to-live для столбца:
Чтобы задать interval, используйте операторы для работы с временными интервалами, например:

TTL для столбца

Когда срок хранения значений в столбце истекает, ClickHouse заменяет их значениями по умолчанию для типа данных этого столбца. Если в части данных истекает срок хранения всех значений столбца, ClickHouse удаляет этот столбец из части данных в файловой системе. Клаузу TTL нельзя использовать для столбцов ключа. Примеры

Создание таблицы с TTL:

Добавление TTL к столбцу существующей таблицы

Изменение TTL для столбца

TTL таблицы

Таблица может иметь выражение для удаления строк с истёкшим TTL и несколько выражений для автоматического перемещения частей между дисками или томами. Когда у строк в таблице истекает TTL, ClickHouse удаляет все соответствующие строки. Для перемещения или повторного сжатия части необходимо, чтобы все её строки удовлетворяли условиям выражения TTL.
После каждого выражения TTL можно указать тип правила TTL. Он определяет действие, которое должно быть выполнено, когда выражение срабатывает (достигает текущего времени):
  • DELETE - удалить истёкшие строки (действие по умолчанию);
  • RECOMPRESS codec_name - повторно сжать часть данных с помощью codec_name;
  • TO DISK 'aaa' - переместить часть на диск aaa;
  • TO VOLUME 'bbb' - переместить часть на диск bbb;
  • GROUP BY - агрегировать истёкшие строки.
Действие DELETE можно использовать вместе с условием WHERE, чтобы удалять только часть истёкших строк на основе условия фильтрации:
Выражение GROUP BY должно быть префиксом первичного ключа таблицы. Если столбец не входит в выражение GROUP BY и не задан явно в предложении SET, то в результирующей строке он содержит произвольное значение из сгруппированных строк (как если бы к нему была применена агрегатная функция any). Примеры

Создание таблицы с TTL:

Изменение TTL у таблицы:

Создание таблицы, в которой срок хранения строк составляет один месяц. Просроченные строки, даты которых приходятся на понедельник, удаляются:

Создание таблицы, в которой для истёкших строк выполняется повторное сжатие:

Создание таблицы, в которой агрегируются истекшие строки. В результирующих строках x содержит максимальное значение среди сгруппированных строк, y — минимальное значение, а d — любое произвольное значение из сгруппированных строк.

Удаление устаревших данных

Данные с истёкшим TTL удаляются, когда ClickHouse выполняет слияние частей данных. Когда ClickHouse обнаруживает, что срок действия данных истёк, он выполняет внеплановое слияние. Чтобы управлять частотой таких слияний, можно задать merge_with_ttl_timeout. Если значение слишком низкое, будет выполняться много внеплановых слияний, которые могут потреблять много ресурсов. Если выполнить запрос SELECT между слияниями, можно получить устаревшие данные. Чтобы этого избежать, используйте запрос OPTIMIZE перед SELECT. См. также

Типы дисков

Помимо локальных блочных устройств, ClickHouse поддерживает следующие типы хранилищ:

Использование нескольких блочных устройств для хранения данных

Введение

Движки таблиц семейства MergeTree могут хранить данные на нескольких блочных устройствах. Например, это может быть полезно, когда данные определённой таблицы естественным образом делятся на «горячие» и «холодные». Самые свежие данные запрашиваются регулярно, но занимают сравнительно немного места. Напротив, исторические данные с длинным хвостом запрашиваются редко. Если доступно несколько дисков, «горячие» данные можно размещать на быстрых дисках (например, NVMe SSD или в памяти), а «холодные» — на относительно медленных (например, HDD). Это применимо ко всем типам дисков, включая S3 и другие диски объектного хранилища. Например, вы можете распределить данные по нескольким S3 бакетам в пределах одного тома или создать многоуровневые политики, которые перемещают данные с локальных дисков в S3. Подробнее см. в разделе Использование S3-дисков с несколькими томами. Часть — это минимальная перемещаемая единица для таблиц с движком MergeTree. Данные, относящиеся к одной части, хранятся на одном диске. Части могут перемещаться между дисками в фоновом режиме (в соответствии с пользовательскими настройками), а также с помощью запросов ALTER.

Термины

  • Диск — блочное устройство, смонтированное в файловую систему.
  • Диск по умолчанию — диск, соответствующий пути, указанному в настройке сервера path.
  • Том — упорядоченный набор одинаковых дисков (аналогично JBOD).
  • Политика хранения — набор томов и правил перемещения данных между ними.
Имена, присвоенные описанным объектам, можно найти в системных таблицах system.storage_policies и system.disks. Чтобы назначить таблице одну из настроенных политик хранения, используйте настройку storage_policy таблиц семейства движков MergeTree.

Конфигурация

Диски, тома и политики хранения должны быть объявлены внутри тега <storage_configuration> в одном из файлов каталога config.d.
Диски также можно объявить в разделе SETTINGS запроса. Это полезно для ad hoc-анализа, чтобы временно подключить диск, который, например, размещён по URL. Подробнее см. в разделе динамическое хранилище.
Структура конфигурации:
Теги:
  • <disk_name_N> — имя диска. Имена всех дисков должны отличаться.
  • path — путь, по которому сервер будет хранить данные (папки data и shadow); должен оканчиваться на ’/’.
  • keep_free_space_bytes — объём свободного места на диске, который нужно зарезервировать.
Порядок описания дисков не важен. Разметка конфигурации политик хранения:
Теги:
  • policy_name_N — имя политики. Имена политик должны быть уникальными.
  • volume_name_N — имя тома. Имена томов должны быть уникальными.
  • disk — диск внутри тома.
  • max_data_part_size_bytes — максимальный размер части, которая может храниться на любом из дисков тома. Если предполагаемый размер слитой части превышает max_data_part_size_bytes, эта часть будет записана на следующий том. По сути, эта возможность позволяет хранить новые/небольшие части на горячем томе (SSD) и перемещать их на холодный том (HDD), когда они становятся большими. Не используйте этот параметр, если в вашей политике только один том.
  • move_factor — когда объем доступного пространства становится меньше этого коэффициента, данные автоматически начинают перемещаться на следующий том, если он есть (по умолчанию 0.1). ClickHouse сортирует существующие части по размеру от большего к меньшему (по убыванию) и выбирает части, суммарный размер которых достаточен для выполнения условия move_factor. Если суммарного размера всех частей недостаточно, будут перемещены все части.
  • perform_ttl_move_on_insert — отключает TTL move при INSERT части. По умолчанию (если параметр включен), если вставляется часть, которая уже подпадает под правило TTL move, она сразу записывается на том/диск, указанный в правиле перемещения. Это может существенно замедлить вставку, если том/диск пункта назначения медленный (например, S3). Если параметр отключен, уже подпадающая под TTL часть записывается на том по умолчанию, а затем сразу перемещается на TTL-том.
  • load_balancing - политика балансировки дисков: round_robin или least_used.
  • least_used_ttl_ms - настраивает тайм-аут (в миллисекундах) обновления доступного пространства на всех дисках (0 - обновлять всегда, -1 - никогда не обновлять, значение по умолчанию — 60000). Обратите внимание: если диск может использоваться только ClickHouse и не подвержен изменению размера файловой системы в режиме online, можно использовать -1; во всех остальных случаях это не рекомендуется, так как со временем это приведет к некорректному распределению пространства.
  • prefer_not_to_merge — этот параметр не следует использовать. Он отключает слияние частей на этом томе (это вредно и приводит к снижению производительности). Когда этот параметр включен (не делайте этого), слияние данных на этом томе запрещено (а это плохо). Это позволяет (хотя вам это не нужно) контролировать (если вам хочется что-то контролировать, вы, скорее всего, делаете что-то не так), как ClickHouse работает с медленными дисками (но ClickHouse знает лучше, поэтому, пожалуйста, не используйте этот параметр).
  • volume_priority — определяет приоритет (порядок), в котором заполняются тома. Меньшее значение означает более высокий приоритет. Значения параметра должны быть натуральными числами и в совокупности покрывать диапазон от 1 до N (где N соответствует наименьшему приоритету) без пропуска чисел.
    • Если помечены все тома, приоритет назначается им в указанном порядке.
    • Если помечены только некоторые тома, тома без метки получают наименьший приоритет, а между собой упорядочиваются в том порядке, в котором они заданы в config.
    • Если не помечен ни один том, их приоритет определяется порядком, в котором они объявлены в конфигурации.
    • Два тома не могут иметь одинаковое значение приоритета.
Примеры конфигурации:
В приведённом примере политика hdd_in_order реализует подход round-robin. Таким образом, эта политика определяет только один том (single), а части хранятся на всех его дисках циклически. Такая политика может быть весьма полезной, если в системе смонтировано несколько одинаковых дисков, но RAID не настроен. Имейте в виду, что каждый отдельный диск сам по себе ненадёжен, и это может потребовать коэффициента репликации 3 или выше. Если в системе доступны разные типы дисков, вместо неё можно использовать политику moving_from_ssd_to_hdd. Том hot состоит из SSD-диска (fast_ssd), а максимальный размер части, которая может храниться на этом томе, составляет 1 ГБ. Все части размером более 1 ГБ будут сразу сохраняться на томе cold, который содержит HDD-диск disk1. Кроме того, как только диск fast_ssd заполнится более чем на 80%, данные будут перенесены на disk1 фоновым процессом. Порядок перечисления томов в рамках политики хранения важен, если хотя бы для одного из перечисленных томов явно не задан параметр volume_priority. Когда том переполняется, данные перемещаются на следующий. Порядок перечисления дисков также важен, поскольку данные записываются на них по очереди. При создании таблицы к ней можно применить одну из настроенных политик хранения:
Политика хранения default предполагает использование только одного тома, состоящего из одного диска, указанного в <path>. Вы можете изменить политику хранения после создания таблицы с помощью запроса [ALTER TABLE … MODIFY SETTING]; новая политика должна включать все прежние диски и тома с теми же именами. Количество потоков, выполняющих фоновое перемещение частей, можно изменить с помощью настройки background_move_pool_size.

Подробности

В случае таблиц MergeTree данные записываются на диск разными способами:
  • В результате вставки (запрос INSERT).
  • Во время фоновых слияний и мутаций.
  • При загрузке с другой реплики.
  • В результате заморозки партиции ALTER TABLE … FREEZE PARTITION.
Во всех этих случаях, кроме мутаций и заморозки партиции, часть сохраняется на томе и диске в соответствии с заданной политикой хранения:
  1. Выбирается первый том (в порядке объявления), на котором достаточно места для хранения части (unreserved_space > current_part_size) и который допускает хранение частей такого размера (max_data_part_size_bytes > current_part_size).
  2. Внутри этого тома выбирается диск, следующий за тем, который использовался для хранения предыдущего фрагмента данных, и на котором свободного места больше, чем размер части (unreserved_space - keep_free_space_bytes > current_part_size).
На уровне реализации мутации и заморозка партиции используют жесткие ссылки. Жесткие ссылки между разными дисками не поддерживаются, поэтому в таких случаях результирующие части сохраняются на тех же дисках, что и исходные. В фоновом режиме части перемещаются между томами в зависимости от объема свободного места (параметр move_factor) в соответствии с порядком, в котором тома объявлены в файле конфигурации. Данные никогда не переносятся с последнего тома и не переносятся на первый. Для отслеживания фоновых перемещений можно использовать системные таблицы system.part_log (поле type = MOVE_PART) и system.parts (поля path и disk). Кроме того, подробную информацию можно найти в серверных журналах. Пользователь может принудительно переместить часть или партицию с одного тома на другой с помощью запроса ALTER TABLE … MOVE PART|PARTITION … TO VOLUME|DISK …; при этом учитываются все ограничения для фоновых операций. Запрос сам инициирует перемещение и не дожидается завершения фоновых операций. Пользователь получит сообщение об ошибке, если свободного места недостаточно или если не выполнено какое-либо из необходимых условий. Перемещение данных не влияет на репликацию. Поэтому для одной и той же таблицы на разных репликах можно задавать разные политики хранения. После завершения фоновых слияний и мутаций старые части удаляются только спустя некоторое время (old_parts_lifetime). В течение этого времени они не перемещаются на другие тома или диски. Поэтому, пока части не будут окончательно удалены, они по-прежнему учитываются при расчете занятого дискового пространства. Пользователь может равномерно распределять новые крупные части по разным дискам тома JBOD с помощью настройки min_bytes_to_rebalance_partition_over_jbod.

Использование внешнего хранилища для хранения данных

Движки таблиц семейства MergeTree могут хранить данные в S3, AzureBlobStorage и HDFS, используя диски типов s3, azure_blob_storage и hdfs соответственно. Подробнее см. в разделе настройка параметров внешнего хранилища. Пример использования S3 в качестве внешнего хранилища с диском типа s3. Конфигурация:
См. также настройку параметров внешнего хранилища.

Использование S3-дисков с несколькими томами

S3-диски (и другие диски объектного хранилища) можно использовать в политиках хранения с несколькими дисками и томами так же, как и локальные диски. Это позволяет распределять данные по нескольким S3 бакетам в рамках одного тома (по принципу JBOD) или настраивать политики многоуровневого хранения с S3-томами. Например, чтобы распределять данные между двумя S3 бакетами по очереди:
Вы также можете объединить локальные тома и тома S3 в многоуровневой политике, например перемещать данные с локального SSD в S3 по мере их старения:
При использовании use_environment_credentials для аутентификации в S3 учетные данные окружения (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) используются всеми S3-дисками совместно. Использовать разные учетные данные окружения для разных дисков нельзя. Если для каждого S3-диска нужны свои учетные данные, вместо этого укажите явные настройки access_key_id и secret_access_key для каждого диска.
Можно настроить таблицы MergeTree без репликации для сценария с одним пишущим узлом и множеством читающих узлов на общем хранилище. Это обеспечивается автоматическим обновлением списка частей, которое можно настроить на читающих узлах. Обратите внимание, что для этого нужны общие метаданные файловой системы для всех реплик (или table_disk = true с локальным для таблицы диском). См. refresh_parts_interval and table_disk.
конфигурация кэшаВ версиях ClickHouse с 22.3 по 22.7 используется другая конфигурация кэша; если вы используете одну из этих версий, см. раздел использование локального кэша.

Виртуальные столбцы

  • _part — Имя части.
  • _part_index — Последовательный индекс части в результате запроса.
  • _part_starting_offset — Накопительный номер начальной строки части в результате запроса.
  • _part_offset — Номер строки в части.
  • _part_granule_offset — Номер гранулы в части.
  • _partition_id — Имя партиции.
  • _part_uuid — Уникальный идентификатор части (если включена настройка MergeTree assign_part_uuids).
  • _part_data_version — Версия данных части (минимальный номер блока или версия мутации).
  • _partition_value — Значения (кортеж) выражения partition by.
  • _sample_factor — Коэффициент выборки (из запроса).
  • _block_number — Исходный номер блока для строки, назначенный при вставке; сохраняется при слияниях, когда включена настройка enable_block_number_column.
  • _block_offset — Исходный номер строки в блоке, назначенный при вставке; сохраняется при слияниях, когда включена настройка enable_block_offset_column.
  • _disk_name — Имя диска, используемого для хранения.

Статистика столбцов

Статистика объявляется в разделе столбцов запроса CREATE для таблиц семейства *MergeTree*:
Статистикой также можно управлять с помощью команд ALTER:
Эти легковесные статистики содержат агрегированную информацию о распределении значений в столбцах. Статистики хранятся в каждой части и обновляются при каждой вставке. Их можно использовать для оптимизации PREWHERE только при включении set use_statistics = 1.

Отсечение частей на основе статистики

Если включен параметр use_statistics_for_part_pruning, для отсечения частей можно использовать статистику. В настоящее время отсечение частей поддерживается только статистикой basic (и устаревшей статистикой minmax). Когда такая статистика определена для столбца, ClickHouse отслеживает минимальное и максимальное значения этого столбца в каждой части. Отсечение частей позволяет пропускать чтение целых частей данных, если условие фильтра запроса не может соответствовать ни одной строке в такой части. Пример:

Доступные типы статистики столбцов

  • basic Компактный набор однозначных сводных характеристик, вычисляемых по столбцу. В зависимости от типа столбца заполняются следующие компоненты:
    • для любого столбца, значения которого представлены числами (целые числа, числа с плавающей точкой, Decimal*, Date*, DateTime*, Enum*, IPv4, …): минимальное и максимальное значения, которые позволяют оценивать селективность фильтра диапазона и выполнять отсечение частей;
    • для столбцов String и FixedString: суммарная длина в байтах всех значений, отличных от NULL (на её основе можно вычислить среднюю длину строки);
    • для столбцов Nullable и LowCardinality(Nullable): количество значений NULL, которое оптимизатор использует, чтобы исключать строки с NULL из оценок селективности. Одна статистика basic может одновременно заполнять несколько таких компонентов — например, для столбца Nullable(UInt32) она отслеживает и числовые минимум/максимум, и количество значений NULL. По сравнению с minmax, basic дополнительно работает со столбцами String / FixedString и может быть объявлена для обёрток Nullable типов вроде UUID или IPv6 исключительно для отслеживания количества значений NULL.
  • minmax (устарело)
Статистика minmax устарела, и её больше нельзя создавать (CREATE TABLE ... STATISTICS(minmax) и ALTER TABLE ... ADD/MODIFY STATISTICS ... TYPE minmax возвращают ошибку). Существующие таблицы и части со статистикой minmax продолжают работать. Вместо неё используйте статистику basic.
  • tdigest
Статистика типа tdigest требует больших затрат на создание и может замедлять приём данных.
Скетчи TDigest, которые позволяют вычислять приблизительные перцентили (например, 90-й перцентиль) для числовых столбцов.
  • uniq Скетчи BJKST, которые позволяют оценить количество различных значений в столбце. Внутри используется uniq.
  • uniq_v2 Аналогично uniq, но внутри используется uniqCombined(12) (вариант HyperLogLog). Потребляет меньше памяти, чем uniq, и может строиться быстрее.
  • countmin
Статистика типа countmin требует больших затрат на создание и может замедлять приём данных.
Скетчи CountMin, которые позволяют приблизительно оценить частоту каждого значения в столбце.

Поддерживаемые типы данных

Для всего перечисленного выше также поддерживаются обертки Nullable и LowCardinality(Nullable) указанных типов. Basic также можно объявлять для оберток Nullable типов вроде UUID или IPv6 исключительно для отслеживания количества NULL.

Поддерживаемые операции

Для basic в столбцах String / FixedString статистика учитывает только суммарную длину в байтах значений, отличных от NULL (используется для оценки средней длины строк), и количество значений NULL; фильтры диапазона и отсечение частей на её основе не применяются.

Настройки на уровне столбцов

Некоторые настройки MergeTree можно переопределять на уровне столбцов:
  • max_compress_block_size — Максимальный размер блоков несжатых данных перед сжатием при записи в таблицу.
  • min_compress_block_size — Минимальный размер блоков несжатых данных, необходимый для сжатия перед записью следующей метки.
Пример:
Настройки на уровне столбца можно изменить или удалить с помощью ALTER MODIFY COLUMN, например:
  • Удалить SETTINGS из определения столбца:
  • Измените параметр:
  • Сбрасывает одну или несколько настроек, а также удаляет объявление настройки из выражения столбца в CREATE-запросе таблицы.
Последнее изменение 24 июля 2026 г.