Движок таблицы MergeTree
Движок MergeTree и другие движки семейства MergeTree (например, ReplacingMergeTree, AggregatingMergeTree) являются наиболее часто используемыми и наиболее надёжными движками таблиц в ClickHouse.
Движки таблиц семейства MergeTree спроектированы для высокой скорости приёма данных и работы с очень большими объёмами.
Операции вставки создают части таблицы, которые затем объединяются фоновым процессом с другими частями таблицы.
Основные особенности движков таблиц семейства MergeTree.
-
Первичный ключ таблицы определяет порядок сортировки внутри каждой части таблицы (кластерный индекс). При этом первичный ключ указывает не на отдельные строки, а на блоки по 8192 строки, которые называются гранулами. Это делает первичные ключи для очень больших наборов данных достаточно компактными, чтобы оставаться загруженными в основную память, при этом обеспечивая быстрый доступ к данным на диске.
-
Таблицы могут быть разбиты на разделы (партиции) с использованием произвольного выражения секционирования. Исключение разделов (partition pruning) гарантирует, что такие разделы пропускаются при чтении, когда это допускает запрос.
-
Данные могут реплицироваться между несколькими узлами кластера для обеспечения высокой доступности, отказоустойчивости и обновлений без простоя. См. раздел Репликация данных.
-
Движки таблиц
MergeTreeподдерживают различные виды статистики и методы выборочного чтения (sampling), помогающие оптимизировать запросы.
Несмотря на похожее название, движок 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, ORDER BY () неявно добавляется к операторам CREATE TABLE. См. раздел Выбор первичного ключа.
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 — выражение для семплирования (sampling expression). Необязательное выражение.
Если указано, оно должно входить в первичный ключ. Результат этого выражения должен быть беззнаковым целым числом.
Пример: 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
В этом примере мы задаём секционирование по месяцам.
Мы также задаём выражение для выборочного чтения данных в виде хэша по ID пользователя. Это позволяет псевдослучайно распределить данные в таблице для каждого CounterID и EventDate. Если вы укажете предложение SAMPLE при выборке данных, ClickHouse вернёт равномерную псевдослучайную выборку данных для подмножества пользователей.
Параметр index_granularity можно опустить, так как 8192 — это значение по умолчанию.
Устаревший метод создания таблицы
Не используйте этот метод в новых проектах. По возможности переведите старые проекты на метод, описанный выше.
Параметры MergeTree()
date-column— Имя столбца типа Date. ClickHouse автоматически создаёт партиции по месяцам на основе этого столбца. Имена партиций имеют формат"YYYYMM".sampling_expression— Выражение для выборочного чтения данных.(primary, key)— Первичный ключ. Тип: Tuple()index_granularity— Гранулярность индекса. Количество строк данных между «метками» индекса. Значение 8192 подходит для большинства задач.
Пример
Движок MergeTree настраивается так же, как в примере выше для основного метода конфигурации движка.
Хранение данных
Таблица состоит из частей данных, отсортированных по первичному ключу.
При вставке данных в таблицу создаются отдельные части данных, и каждая из них лексикографически сортируется по первичному ключу. Например, если первичный ключ — (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. Принцип NULLS_LAST применяется к значениям NULL в выражении ORDER BY.
Выбор первичного ключа
Количество столбцов в первичном ключе явно не ограничено. В зависимости от структуры данных вы можете включать больше или меньше столбцов в первичный ключ. Это может:
-
Повысить производительность индекса.
Если первичный ключ —
(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.
Ключ партиционирования по месяцам позволяет читать только те блоки данных, которые содержат даты из нужного диапазона. В этом случае блок данных может содержать данные за множество дат (вплоть до целого месяца). Внутри блока данные отсортированы по первичному ключу, который может не содержать дату в качестве первого столбца. Из-за этого использование запроса только с условием по дате, без указания префикса первичного ключа, приведёт к чтению большего объёма данных, чем при выборке за одну дату.
Использование индекса для частично-монотонных первичных ключей
Рассмотрим, например, дни месяца. Они образуют монотонную последовательность в пределах одного месяца, но не являются монотонными на более длительных промежутках времени. Это частично-монотонная последовательность. Если пользователь создаёт таблицу с частично-монотонным первичным ключом, ClickHouse создаёт разреженный индекс как обычно. Когда пользователь выбирает данные из такой таблицы, ClickHouse анализирует условия запроса. Если пользователь хочет получить данные между двумя метками индекса и обе эти метки попадают в один месяц, ClickHouse может использовать индекс в этом конкретном случае, потому что он может вычислить расстояние между параметрами запроса и метками индекса.
ClickHouse не может использовать индекс, если значения первичного ключа в заданном в параметрах запроса диапазоне не образуют монотонную последовательность. В этом случае ClickHouse использует полное сканирование.
ClickHouse применяет эту логику не только к последовательностям дней месяца, но и к любому первичному ключу, который представляет частично-монотонную последовательность.
Индексы пропуска данных
Объявление индекса указывается в разделе COLUMNS оператора CREATE.
Для таблиц из семейства *MergeTree можно задать индексы пропуска данных (data skipping indices).
Эти индексы агрегируют некоторую информацию об указанном выражении по блокам, которые состоят из гранул размера granularity_value (размер гранулы задаётся с помощью настройки index_granularity в движке таблицы). Затем эти агрегаты используются в запросах SELECT для уменьшения объёма данных, считываемых с диска, за счёт пропуска крупных блоков данных, в которых условие секции WHERE не может быть выполнено.
Секцию GRANULARITY можно опустить, значение granularity_value по умолчанию равно 1.
Пример
ClickHouse может использовать индексы из примера, чтобы сократить объём данных, считываемых с диска, в следующих запросах:
Индексы пропуска данных также могут создаваться для составных столбцов:
Типы пропускающих индексов
Движок таблицы MergeTree поддерживает следующие типы пропускающих индексов.
Подробнее об использовании пропускающих индексов для оптимизации производительности
см. в разделе "Понимание пропускающих индексов данных в ClickHouse".
- индекс
MinMax - индекс
Set - индекс
bloom_filter - индекс
ngrambf_v1 - индекс
tokenbf_v1
Индекс MinMax
Для каждой гранулы индекса сохраняются минимальные и максимальные значения выражения.
(Если выражение имеет тип tuple, сохраняются минимальные и максимальные значения для каждого элемента кортежа.)
Set
Для каждой гранулы индекса сохраняется не более max_rows уникальных значений указанного выражения.
max_rows = 0 означает «хранить все уникальные значения».
Фильтр Блума
Для каждой гранулы индекса хранится фильтр Блума по указанным столбцам.
Параметр false_positive_rate может принимать значение от 0 до 1 (по умолчанию: 0.025) и задаёт вероятность положительного срабатывания (что увеличивает объём считываемых данных).
Поддерживаются следующие типы данных:
(U)Int*Float*EnumDateDateTimeStringFixedStringArrayLowCardinalityNullableUUIDMap
N-граммовый Bloom-фильтр
Для каждой гранулы индекса хранится Bloom-фильтр по n-граммам указанных столбцов.
| Parameter | Description |
|---|---|
n | размер n-граммы |
size_of_bloom_filter_in_bytes | Размер фильтра Блума в байтах. Здесь можно использовать большое значение, например 256 или 512, поскольку оно хорошо сжимается. |
number_of_hash_functions | Количество хеш-функций, используемых в фильтре Блума. |
random_seed | Начальное значение (seed) для хеш-функций фильтра Блума. |
Этот индекс работает только со следующими типами данных:
Чтобы оценить параметры ngrambf_v1, вы можете использовать следующие пользовательские функции (UDF).
Чтобы использовать эти функции, необходимо указать не менее двух параметров:
total_number_of_all_gramsprobability_of_false_positives
Например, в грануле есть 4300 n-грамм, и вы ожидаете, что вероятность ложных срабатываний будет меньше 0.0001.
Остальные параметры можно затем оценить, выполнив следующие запросы:
Разумеется, вы также можете использовать эти функции для оценки параметров и в других условиях. Приведённые выше функции соответствуют калькулятору фильтра Блума, доступному по адресу здесь.
Фильтр Блума по токенам
Фильтр Блума по токенам аналогичен ngrambf_v1, но вместо n-грамм хранит токены (последовательности, разделённые небуквенно-цифровыми символами).
Разрежённый n-граммный фильтр Блума
Разрежённый n-граммный фильтр Блума аналогичен ngrambf_v1, но использует токены разрежённых n-грамм вместо n-грамм.
Текстовый индекс
Поддерживает полнотекстовый поиск; подробности см. здесь.
Сходство векторов
Поддерживает приближённый поиск ближайших соседей, подробнее см. здесь.
Поддержка функций
Условия в предложении WHERE содержат вызовы функций, которые работают со столбцами. Если столбец является частью индекса, ClickHouse пытается использовать этот индекс при вычислении этих функций. ClickHouse поддерживает различные подмножества функций для работы с индексами.
Индексы типа set могут использоваться всеми функциями. Остальные типы индексов поддерживаются следующим образом:
| Функция (оператор) / Индекс | первичный ключ | minmax | ngrambf_v1 | tokenbf_v1 | bloom_filter | sparse_grams | текст |
|---|---|---|---|---|---|---|---|
| равно (=, ==) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| notEquals(!=, <>) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| like | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | ✔ |
| notLike | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | ✔ |
| match | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✔ |
| startsWith | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | ✔ |
| endsWith | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✔ |
| multiSearchAny | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ | ✗ |
| in | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| notIn | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
меньше (<) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
greater (>) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
lessOrEquals (<=) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
greaterOrEquals (>=) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
| empty | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
| notEmpty | ✗ | ✔ | ✗ | ✗ | ✗ | ✔ | ✗ |
| has | ✗ | ✗ | ✔ | ✔ | ✔ | ✔ | ✔ |
| hasAny | ✗ | ✗ | ✔ | ✔ | ✔ | ✔ | ✗ |
| hasAll | ✗ | ✗ | ✔ | ✔ | ✔ | ✔ | ✗ |
| hasToken | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ |
| hasTokenOrNull | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ |
hasTokenCaseInsensitive (*) | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ |
hasTokenCaseInsensitiveOrNull (*) | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ |
| hasAnyTokens | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
| hasAllTokens | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
| mapContains | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
Функции с константным аргументом, значение которого меньше размера n-граммы, не могут использоваться индексом ngrambf_v1 для оптимизации запросов.
(*) Чтобы hasTokenCaseInsensitive и hasTokenCaseInsensitiveOrNull были эффективны, индекс tokenbf_v1 должен быть создан по данным в нижнем регистре, например: INDEX idx (lower(str_col)) TYPE tokenbf_v1(512, 3, 0).
У фильтров Блума возможны ложноположительные срабатывания, поэтому индексы ngrambf_v1, tokenbf_v1, sparse_grams и bloom_filter не могут использоваться для оптимизации запросов, в которых ожидается, что результат функции будет ложным.
Например:
- Может быть оптимизировано:
s LIKE '%test%'NOT s NOT LIKE '%test%'s = 1NOT s != 1startsWith(s, 'test')
- Не может быть оптимизировано:
NOT s LIKE '%test%'s NOT LIKE '%test%'NOT s = 1s != 1NOT startsWith(s, 'test')
Проекции
Проекции похожи на materialized views, но определяются на уровне частей таблицы (parts). Они обеспечивают гарантии согласованности, а также автоматическое использование в запросах.
При использовании проекций следует также учитывать настройку force_optimize_projection.
Проекции не поддерживаются в операторах SELECT с модификатором FINAL.
Запрос проекции
Запрос проекции определяет проекцию. Он неявно выбирает данные из родительской таблицы. Синтаксис
Проекции можно изменять или удалять с помощью оператора ALTER.
Хранение проекций
Проекции хранятся внутри каталога части. По сути это похоже на индекс, но включает подкаталог, в котором хранится часть анонимной таблицы MergeTree. Таблица задаётся запросом, определяющим проекцию. Если в определении есть предложение GROUP BY, базовый движок хранения становится AggregatingMergeTree, и все агрегатные функции приводятся к типу AggregateFunction. Если есть предложение ORDER BY, таблица MergeTree использует его как выражение первичного ключа. Во время процесса слияния часть проекции объединяется с использованием процедуры слияния её движка хранения. Контрольная сумма части родительской таблицы объединяется с частью проекции. Остальные операции обслуживания аналогичны операциям для skip-индексов.
Анализ запросов
- Проверьте, может ли проекция быть использована для ответа на данный запрос, то есть даёт ли она тот же результат, что и запрос к базовой таблице.
- Выберите оптимальное соответствие, для которого нужно прочитать наименьшее количество гранул.
- Конвейер обработки запроса, использующий проекции, будет отличаться от конвейера, работающего с исходными частями. Если в некоторых частях проекция отсутствует, можно добавить конвейер, чтобы «спроецировать» её на лету.
Одновременный доступ к данным
Для одновременного доступа к таблице используется многоверсионность. Иными словами, когда таблица одновременно читается и обновляется, данные читаются из набора частей, актуального на момент выполнения запроса. Длительные блокировки отсутствуют. Вставки не мешают операциям чтения.
Чтение из таблицы автоматически распараллеливается.
TTL для столбцов и таблиц
Определяет время жизни значений.
Выражение TTL может быть задано как для всей таблицы, так и для каждого отдельного столбца. TTL на уровне таблицы также может задавать логику автоматического перемещения данных между дисками и томами, а также перекомпрессии частей, в которых срок жизни всех данных истёк.
Выражения должны вычисляться в значение типа данных Date, Date32, DateTime или DateTime64.
Синтаксис
Установка времени жизни для столбца:
Чтобы задать interval, используйте операторы интервалов времени, например:
TTL столбца
Когда срок жизни значений в столбце истекает, ClickHouse заменяет их значениями по умолчанию для типа данных столбца. Если срок жизни всех значений столбца в части данных истекает, ClickHouse удаляет этот столбец из соответствующей части данных в файловой системе.
Предложение 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.
См. также
- настройка ttl_only_drop_parts
Типы дисков
Помимо локальных блочных устройств, ClickHouse поддерживает следующие типы хранилищ:
s3для S3 и MinIOgcsдля GCSblob_storage_diskдля Azure Blob Storagehdfsдля HDFSwebдля режима только чтения с вебаcacheдля локального кэшированияs3_plainдля резервных копий в S3s3_plain_rewritableдля неизменяемых, нереплицируемых таблиц в S3
Использование нескольких блочных устройств для хранения данных
Введение
Семейство движков таблиц MergeTree может хранить данные на нескольких блочных устройствах. Например, это может быть полезно, когда данные определённой таблицы фактически разделены на «горячие» и «холодные». Самые свежие данные запрашиваются регулярно, но занимают небольшой объём. Напротив, большой «хвост» исторических данных запрашивается редко. Если доступно несколько дисков, «горячие» данные могут располагаться на быстрых дисках (например, NVMe SSD или в памяти), а «холодные» — на относительно медленных (например, HDD).
Часть данных (data part) — минимальная единица, которую можно перемещать, для таблиц на движке MergeTree. Данные, принадлежащие одной части, хранятся на одном диске. Части данных могут перемещаться между дисками в фоновом режиме (в соответствии с пользовательскими настройками), а также с помощью запросов ALTER.
Термины
- Диск — блочное устройство, смонтированное к файловой системе.
- Диск по умолчанию — диск, на котором расположен путь, указанный в серверной настройке path.
- Том — упорядоченный набор одинаковых дисков (аналогично JBOD).
- Политика хранения — набор томов и правил перемещения данных между ними.
Названия описанных сущностей можно найти в системных таблицах system.storage_policies и system.disks. Чтобы применить одну из настроенных политик хранения к таблице, используйте настройку storage_policy для таблиц семейства движков MergeTree.
Конфигурация
Диски, тома и политики хранения должны быть объявлены внутри тега <storage_configuration> или в файле в каталоге config.d.
Диски также могут быть объявлены в секции SETTINGS запроса. Это полезно
для разового анализа, когда нужно временно подключить диск, который, например, доступен по URL-адресу.
См. раздел dynamic storage для получения дополнительной информации.
Структура конфигурации:
Теги:
<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 при INSERT части данных. По умолчанию (если включено), если мы вставляем часть данных, которая уже просрочена по правилу перемещения TTL, она сразу попадает на том/диск, указанный в правиле перемещения. Это может существенно замедлить вставку, если целевой том/диск медленный (например, S3). Если выключено, то уже просроченная часть данных записывается на том по умолчанию, а затем сразу перемещается на том, указанный в правиле TTL.load_balancing— политика балансировки дисков:round_robinилиleast_used.least_used_ttl_ms— настройка таймаута (в миллисекундах) для обновления информации о доступном пространстве на всех дисках (0— всегда обновлять,-1— никогда не обновлять, по умолчанию60000). Обратите внимание: если диск может использоваться только ClickHouse и не подвержен онлайн-изменению размера файловой системы (расширению/сжатию), вы можете использовать-1; во всех остальных случаях это не рекомендуется, так как в итоге это приведёт к некорректному распределению пространства.prefer_not_to_merge— Не следует использовать этот параметр. Отключает слияние частей данных на этом томе (это вредно и приводит к деградации производительности). При включённом параметре (не делайте этого) слияние данных на этом томе не допускается (что плохо). Это позволяет (но вам это не нужно) управлять (если вы хотите что‑то контролировать, вы совершаете ошибку) тем, как ClickHouse работает с медленными дисками (но ClickHouse знает лучше, поэтому, пожалуйста, не используйте этот параметр).volume_priority— Определяет приоритет (порядок), в котором заполняются тома. Меньшее значение означает более высокий приоритет. Значения параметра должны быть натуральными числами и совместно покрывать диапазон от 1 до N (для самого низкого приоритета) без пропусков.- Если все тома помечены, они получают приоритет в указанном порядке.
- Если помечены только некоторые тома, те, у которых нет метки, имеют самый низкий приоритет и получают приоритет в порядке, в котором они определены в конфигурации.
- Если ни один том не помечен, их приоритет задаётся в соответствии с порядком, в котором они объявлены в конфигурации.
- Два тома не могут иметь одинаковое значение приоритета.
Примеры конфигурации:
В приведённом примере политика 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.
Во всех этих случаях, за исключением мутаций и заморозки партиций, часть данных сохраняется на томе и диске в соответствии с заданной политикой хранения:
- Выбирается первый том (в порядке объявления), у которого достаточно свободного дискового пространства для хранения части (
unreserved_space > current_part_size) и который допускает хранение частей заданного размера (max_data_part_size_bytes > current_part_size). - Внутри этого тома выбирается тот диск, который следует за диском, использованным для хранения предыдущей части данных, и у которого свободное пространство больше размера части (
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.
Фрагмент конфигурации:
См. также настройку вариантов внешних хранилищ.
Версии ClickHouse с 22.3 по 22.7 используют другую конфигурацию кэша, см. использование локального кэша, если вы используете одну из этих версий.
Виртуальные столбцы
_part— Имя парта._part_index— Последовательный индекс парта в результате запроса._part_starting_offset— Кумулятивный номер первой строки парта в результате запроса._part_offset— Номер строки в парте._part_granule_offset— Номер гранулы в парте._partition_id— Имя партиции._part_uuid— Уникальный идентификатор парта (если включена настройка MergeTreeassign_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— Имя диска, используемого для хранения.
Статистика по столбцам
Объявление статистики задаётся в секции COLUMNS запроса CREATE для таблиц из семейства *MergeTree* при включённой настройке set allow_experimental_statistics = 1.
Мы также можем изменять статистику с помощью операторов ALTER.
Эта легковесная статистика агрегирует информацию о распределении значений в столбцах. Статистика хранится в каждой части и обновляется при каждой вставке.
Её можно использовать для оптимизации PREWHERE только при включённом параметре set allow_statistics_optimize = 1.
Доступные типы статистики по столбцам
-
MinMaxМинимальное и максимальное значения столбца, что позволяет оценивать селективность диапазонных фильтров по числовым столбцам.
Синтаксис:
minmax -
TDigestСкетчи TDigest, которые позволяют вычислять приблизительные перцентили (например, 90-й перцентиль) для числовых столбцов.
Синтаксис:
tdigest -
UniqСкетчи HyperLogLog, которые позволяют оценить, сколько различных значений содержит столбец.
Синтаксис:
uniq -
CountMinСкетчи CountMin, которые предоставляют приблизительный подсчёт частоты каждого значения в столбце.
Синтаксис:
countmin
Поддерживаемые типы данных
| (U)Int*, Float*, Decimal(), Date, Boolean, Enum* | String или FixedString | |
|---|---|---|
| CountMin | ✔ | ✔ |
| MinMax | ✔ | ✗ |
| TDigest | ✔ | ✗ |
| Uniq | ✔ | ✔ |
Поддерживаемые операции
| Фильтры равенства (==) | Фильтры по диапазону (>, >=, <, <=) | |
|---|---|---|
| CountMin | ✔ | ✗ |
| MinMax | ✗ | ✔ |
| TDigest | ✗ | ✔ |
| Uniq | ✔ | ✗ |
Параметры на уровне столбцов
Некоторые настройки MergeTree можно переопределять на уровне столбцов:
max_compress_block_size— максимальный размер блоков несжатых данных перед их сжатием при записи в таблицу.min_compress_block_size— минимальный размер блоков несжатых данных, необходимый для сжатия при записи следующей метки.
Пример:
Настройки для отдельных столбцов можно изменять или удалять с помощью ALTER MODIFY COLUMN, например:
- Удалить
SETTINGSиз определения столбца:
- Измените параметр:
- Сбрасывает одну или несколько настроек, а также удаляет объявление настройки в определении столбца в запросе CREATE для таблицы.