Перейти к основному содержанию
Перейти к основному содержанию

Движок таблицы MergeTree

Движок MergeTree и другие движки семейства MergeTree (например, ReplacingMergeTree, AggregatingMergeTree) являются наиболее часто используемыми и наиболее надёжными движками таблиц в ClickHouse.

Движки таблиц семейства MergeTree спроектированы для высокой скорости приёма данных и работы с очень большими объёмами. Операции вставки создают части таблицы, которые затем объединяются фоновым процессом с другими частями таблицы.

Основные особенности движков таблиц семейства MergeTree.

  • Первичный ключ таблицы определяет порядок сортировки внутри каждой части таблицы (кластерный индекс). При этом первичный ключ указывает не на отдельные строки, а на блоки по 8192 строки, которые называются гранулами. Это делает первичные ключи для очень больших наборов данных достаточно компактными, чтобы оставаться загруженными в основную память, при этом обеспечивая быстрый доступ к данным на диске.

  • Таблицы могут быть разбиты на разделы (партиции) с использованием произвольного выражения секционирования. Исключение разделов (partition pruning) гарантирует, что такие разделы пропускаются при чтении, когда это допускает запрос.

  • Данные могут реплицироваться между несколькими узлами кластера для обеспечения высокой доступности, отказоустойчивости и обновлений без простоя. См. раздел Репликация данных.

  • Движки таблиц MergeTree поддерживают различные виды статистики и методы выборочного чтения (sampling), помогающие оптимизировать запросы.

Примечание

Несмотря на похожее название, движок Merge отличается от движков *MergeTree.

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

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [[NOT] NULL] [DEFAULT|MATERIALIZED|ALIAS|EPHEMERAL expr1] [COMMENT ...] [CODEC(codec1)] [STATISTICS(stat1)] [TTL expr1] [PRIMARY KEY] [SETTINGS (name = value, ...)],
    name2 [type2] [[NOT] NULL] [DEFAULT|MATERIALIZED|ALIAS|EPHEMERAL expr2] [COMMENT ...] [CODEC(codec2)] [STATISTICS(stat2)] [TTL expr2] [PRIMARY KEY] [SETTINGS (name = value, ...)],
    ...
    INDEX index_name1 expr1 TYPE type1(...) [GRANULARITY value1],
    INDEX index_name2 expr2 TYPE type2(...) [GRANULARITY value2],
    ...
    PROJECTION projection_name_1 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY]),
    PROJECTION projection_name_2 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY])
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr
    [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx' [, ...] ]
    [WHERE conditions]
    [GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ] ]
[SETTINGS name = value, ...]

Подробное описание параметров см. в описании команды 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

ENGINE MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity=8192

В этом примере мы задаём секционирование по месяцам.

Мы также задаём выражение для выборочного чтения данных в виде хэша по ID пользователя. Это позволяет псевдослучайно распределить данные в таблице для каждого CounterID и EventDate. Если вы укажете предложение SAMPLE при выборке данных, ClickHouse вернёт равномерную псевдослучайную выборку данных для подмножества пользователей.

Параметр index_granularity можно опустить, так как 8192 — это значение по умолчанию.

Устаревший метод создания таблицы
Примечание

Не используйте этот метод в новых проектах. По возможности переведите старые проекты на метод, описанный выше.

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
  name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
  name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
  ...
) ENGINE [=] MergeTree(date-column [, sampling_expression], (primary, key), index_granularity)

Параметры MergeTree()

  • date-column — Имя столбца типа Date. ClickHouse автоматически создаёт партиции по месяцам на основе этого столбца. Имена партиций имеют формат "YYYYMM".
  • sampling_expression — Выражение для выборочного чтения данных.
  • (primary, key) — Первичный ключ. Тип: Tuple()
  • index_granularity — Гранулярность индекса. Количество строк данных между «метками» индекса. Значение 8192 подходит для большинства задач.

Пример

MergeTree(EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID)), 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). В этом случае сортировку и индекс можно представить следующим образом:

Whole data:     [---------------------------------------------]
CounterID:      [aaaaaaaaaaaaaaaaaabbbbcdeeeeeeeeeeeeefgggggggghhhhhhhhhiiiiiiiiikllllllll]
Date:           [1111111222222233331233211111222222333211111112122222223111112223311122333]
Marks:           |      |      |      |      |      |      |      |      |      |      |
                a,1    a,2    a,3    b,3    e,2    e,3    g,1    h,2    i,1    i,3    l,3
Marks numbers:   0      1      2      3      4      5      6      7      8      9      10

Если в запросе к данным указано:

  • 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 с фиксированным префиксом по столбцам или выражениям, входящим в первичный ключ или ключ партиционирования, или по определённым частично повторяющимся функциям этих столбцов, или логические комбинации этих выражений.

Таким образом, можно быстро выполнять запросы по одному или нескольким диапазонам первичного ключа. В этом примере запросы будут выполняться быстро при выборке по конкретному тегу отслеживания, по конкретному тегу и диапазону дат, по конкретному тегу и дате, по нескольким тегам с диапазоном дат и так далее.

Рассмотрим движок, настроенный следующим образом:

ENGINE MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate)
SETTINGS index_granularity=8192

В этом случае в запросах:

SELECT count() FROM table
WHERE EventDate = toDate(now())
AND CounterID = 34

SELECT count() FROM table
WHERE EventDate = toDate(now())
AND (CounterID = 34 OR CounterID = 42)

SELECT count() FROM table
WHERE ((EventDate >= toDate('2014-01-01')
AND EventDate <= toDate('2014-01-31')) OR EventDate = toDate('2014-05-01'))
AND CounterID IN (101500, 731962, 160656)
AND (CounterID = 101500 OR EventDate != toDate('2014-05-01'))

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

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

В приведённом ниже примере индекс использоваться не будет.

SELECT count() FROM table WHERE CounterID = 34 OR URL LIKE '%upyachka%'

Чтобы проверить, может ли ClickHouse использовать индекс при выполнении запроса, используйте настройки force_index_by_date и force_primary_key.

Ключ партиционирования по месяцам позволяет читать только те блоки данных, которые содержат даты из нужного диапазона. В этом случае блок данных может содержать данные за множество дат (вплоть до целого месяца). Внутри блока данные отсортированы по первичному ключу, который может не содержать дату в качестве первого столбца. Из-за этого использование запроса только с условием по дате, без указания префикса первичного ключа, приведёт к чтению большего объёма данных, чем при выборке за одну дату.

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

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

ClickHouse не может использовать индекс, если значения первичного ключа в диапазоне параметров запроса не образуют монотонную последовательность. В этом случае ClickHouse использует полное сканирование.

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

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

Объявление индекса указывается в разделе COLUMNS оператора CREATE.

INDEX index_name expr TYPE type(...) [GRANULARITY granularity_value]

Для таблиц из семейства *MergeTree можно задать индексы пропуска данных (data skipping indices).

Эти индексы агрегируют некоторую информацию об указанном выражении по блокам, которые состоят из гранул размера granularity_value (размер гранулы задаётся с помощью настройки index_granularity в движке таблицы). Затем эти агрегаты используются в запросах SELECT для уменьшения объёма данных, считываемых с диска, за счёт пропуска крупных блоков данных, в которых условие секции WHERE не может быть выполнено.

Секцию GRANULARITY можно опустить, значение granularity_value по умолчанию равно 1.

Пример

CREATE TABLE table_name
(
    u64 UInt64,
    i32 Int32,
    s String,
    ...
    INDEX idx1 u64 TYPE bloom_filter GRANULARITY 3,
    INDEX idx2 u64 * i32 TYPE minmax GRANULARITY 3,
    INDEX idx3 u64 * length(s) TYPE set(1000) GRANULARITY 4
) ENGINE = MergeTree()
...

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

SELECT count() FROM table WHERE u64 == 10;
SELECT count() FROM table WHERE u64 * i32 >= 1234
SELECT count() FROM table WHERE u64 * length(s) == 1234

Индексы пропуска данных также могут создаваться для составных столбцов:

-- on columns of type Map:
INDEX map_key_index mapKeys(map_column) TYPE bloom_filter
INDEX map_value_index mapValues(map_column) TYPE bloom_filter

-- on columns of type Tuple:
INDEX tuple_1_index tuple_column.1 TYPE bloom_filter
INDEX tuple_2_index tuple_column.2 TYPE bloom_filter

-- on columns of type Nested:
INDEX nested_1_index col.nested_col1 TYPE bloom_filter
INDEX nested_2_index col.nested_col2 TYPE bloom_filter

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

Движок таблицы MergeTree поддерживает следующие типы индексов пропуска данных. Подробнее о том, как индексы пропуска данных могут использоваться для оптимизации производительности, см. раздел «Понимание индексов пропуска данных в ClickHouse»(/optimize/skipping-indexes).

Индекс MinMax

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

minmax

Set

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

set(max_rows)

Фильтр Блума

Для каждой гранулы индекса хранится фильтр Блума для указанных столбцов.

bloom_filter([false_positive_rate])

Параметр false_positive_rate может принимать значение от 0 до 1 (по умолчанию: 0.025) и задает вероятность генерации ложноположительного срабатывания (что увеличивает объем считываемых данных).

Поддерживаются следующие типы данных:

  • (U)Int*
  • Float*
  • Enum
  • Date
  • DateTime
  • String
  • FixedString
  • Array
  • LowCardinality
  • Nullable
  • UUID
  • Map
Тип данных Map: указание создания индекса по ключам или значениям

Для типа данных Map клиент может указать, должен ли индекс создаваться по ключам или по значениям, используя функции mapKeys или mapValues.

N-граммный фильтр Блума

Каждая гранула индекса хранит фильтр Блума для n-грамм указанных столбцов.

ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
ПараметрОписание
nразмер n-граммы
size_of_bloom_filter_in_bytesразмер фильтра Блума в байтах. Можно использовать большое значение, например, 256 или 512, потому что он хорошо сжимается.
number_of_hash_functionsколичество хэш-функций, используемых в фильтре Блума.
random_seedначальное значение (seed) для хэш-функций фильтра Блума.

Этот индекс работает только со следующими типами данных:

Для оценки параметров ngrambf_v1 вы можете использовать следующие пользовательские функции (UDF).

CREATE FUNCTION bfEstimateFunctions [ON CLUSTER cluster]
AS
(total_number_of_all_grams, size_of_bloom_filter_in_bits) -> round((size_of_bloom_filter_in_bits / total_number_of_all_grams) * log(2));

CREATE FUNCTION bfEstimateBmSize [ON CLUSTER cluster]
AS
(total_number_of_all_grams,  probability_of_false_positives) -> ceil((total_number_of_all_grams * log(probability_of_false_positives)) / log(1 / pow(2, log(2))));

CREATE FUNCTION bfEstimateFalsePositive [ON CLUSTER cluster]
AS
(total_number_of_all_grams, number_of_hash_functions, size_of_bloom_filter_in_bytes) -> pow(1 - exp(-number_of_hash_functions/ (size_of_bloom_filter_in_bytes / total_number_of_all_grams)), number_of_hash_functions);

CREATE FUNCTION bfEstimateGramNumber [ON CLUSTER cluster]
AS
(number_of_hash_functions, probability_of_false_positives, size_of_bloom_filter_in_bytes) -> ceil(size_of_bloom_filter_in_bytes / (-number_of_hash_functions / log(1 - exp(log(probability_of_false_positives) / number_of_hash_functions))))

Чтобы использовать эти функции, необходимо указать как минимум два параметра:

  • total_number_of_all_grams
  • probability_of_false_positives

Например, в грануле содержится 4300 n-грамм, и вы ожидаете, что вероятность ложных срабатываний будет меньше 0.0001. Остальные параметры затем можно оценить, выполнив следующие запросы:

--- estimate number of bits in the filter
SELECT bfEstimateBmSize(4300, 0.0001) / 8 AS size_of_bloom_filter_in_bytes;

┌─size_of_bloom_filter_in_bytes─┐
│                         10304 │
└───────────────────────────────┘

--- estimate number of hash functions
SELECT bfEstimateFunctions(4300, bfEstimateBmSize(4300, 0.0001)) as number_of_hash_functions

┌─number_of_hash_functions─┐
│                       13 │
└──────────────────────────┘

Разумеется, вы также можете использовать эти функции для оценки параметров и в других условиях. Эти функции соответствуют калькулятору фильтра Блума, доступному здесь.

Блум-фильтр по токенам

Блум-фильтр по токенам аналогичен ngrambf_v1, но хранит токены (последовательности, разделённые символами, не являющимися буквенно-цифровыми), а не n-граммы.

tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)

Блум-фильтр по разрежённым граммам

Блум-фильтр по разрежённым граммам аналогичен ngrambf_v1, но использует токены разрежённых грамм вместо n-грамм.

sparse_grams(min_ngram_length, max_ngram_length, min_cutoff_length, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)

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

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

Сходство векторов

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

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

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

Индексы типа set могут использоваться всеми функциями. Остальные типы индексов поддерживаются следующим образом:

Функция (оператор) / Индекспервичный ключminmaxngrambf_v1tokenbf_v1bloom_filtersparse_gramsтекст
равно (=, ==)
notEquals(!=, <>)
like
notLike
match
startsWith
endsWith
multiSearchAny
in
notIn
меньше (<)
больше (>)
меньше или равно (<=)
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 = 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, но определяются на уровне частей таблицы (parts). Они обеспечивают гарантии согласованности, а также автоматическое использование в запросах.

Примечание

При использовании проекций следует также учитывать настройку force_optimize_projection.

Проекции не поддерживаются в операторах SELECT с модификатором FINAL.

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

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

SELECT <column list expr> [GROUP BY] <group keys expr> [ORDER BY] <expr>

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

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

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

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

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

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

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

Чтение из таблицы автоматически распараллеливается.

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

Определяет время жизни значений.

Выражение TTL может быть задано как для всей таблицы, так и для каждого отдельного столбца. TTL на уровне таблицы также может задавать логику автоматического перемещения данных между дисками и томами, а также перекомпрессии частей, в которых срок жизни всех данных истёк.

Выражения должны вычисляться в значение типа данных Date, Date32, DateTime или DateTime64.

Синтаксис

Установка времени жизни для столбца:

TTL time_column
TTL time_column + interval

Чтобы задать interval, используйте операторы интервалов времени, например:

TTL date_time + INTERVAL 1 MONTH
TTL date_time + INTERVAL 15 HOUR

TTL для столбца

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

Предложение TTL нельзя использовать для ключевых столбцов.

Примеры

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

CREATE TABLE tab
(
    d DateTime,
    a Int TTL d + INTERVAL 1 MONTH,
    b Int TTL d + INTERVAL 1 MONTH,
    c String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d;

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

ALTER TABLE tab
    MODIFY COLUMN
    c String TTL d + INTERVAL 1 DAY;

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

ALTER TABLE tab
    MODIFY COLUMN
    c String TTL d + INTERVAL 1 MONTH;

Табличный TTL

Таблица может содержать выражение для удаления просроченных строк и несколько выражений для автоматического перемещения частей между дисками или томами. Когда срок жизни строк в таблице истекает, ClickHouse удаляет все соответствующие строки. Для перемещения или перекомпрессии частей все строки части должны удовлетворять условию выражения TTL.

TTL expr
    [DELETE|RECOMPRESS codec_name1|TO DISK 'xxx'|TO VOLUME 'xxx'][, DELETE|RECOMPRESS codec_name2|TO DISK 'aaa'|TO VOLUME 'bbb'] ...
    [WHERE conditions]
    [GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ]

Тип правила TTL может быть указан после каждого выражения TTL. Он определяет действие, которое будет выполнено, когда выражение выполняется (его значение достигает текущего времени):

  • DELETE — удалить истекшие строки (действие по умолчанию);
  • RECOMPRESS codec_name — перекомпрессировать часть данных с использованием codec_name;
  • TO DISK 'aaa' — переместить часть данных на диск aaa;
  • TO VOLUME 'bbb' — переместить часть данных на диск bbb;
  • GROUP BY — агрегировать истекшие строки.

Действие DELETE можно использовать вместе с предложением WHERE, чтобы удалить только некоторые из истекших строк на основе условия фильтрации:

TTL time_column + INTERVAL 1 MONTH DELETE WHERE column = 'value'

Выражение GROUP BY должно быть префиксом первичного ключа таблицы.

Если столбец не является частью выражения GROUP BY и не задан явно в предложении SET, то в результирующей строке он содержит произвольное значение из сгруппированных строк (как если бы к нему была применена агрегатная функция any).

Примеры

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

CREATE TABLE tab
(
    d DateTime,
    a Int
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d
TTL d + INTERVAL 1 MONTH DELETE,
    d + INTERVAL 1 WEEK TO VOLUME 'aaa',
    d + INTERVAL 2 WEEK TO DISK 'bbb';

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

ALTER TABLE tab
    MODIFY TTL d + INTERVAL 1 DAY;

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

CREATE TABLE table_with_where
(
    d DateTime,
    a Int
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d
TTL d + INTERVAL 1 MONTH DELETE WHERE toDayOfWeek(d) = 1;

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

CREATE TABLE table_for_recompression
(
    d DateTime,
    key UInt64,
    value String
) ENGINE MergeTree()
ORDER BY tuple()
PARTITION BY key
TTL d + INTERVAL 1 MONTH RECOMPRESS CODEC(ZSTD(17)), d + INTERVAL 1 YEAR RECOMPRESS CODEC(LZ4HC(10))
SETTINGS min_rows_for_wide_part = 0, min_bytes_for_wide_part = 0;

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

CREATE TABLE table_for_aggregation
(
    d DateTime,
    k1 Int,
    k2 Int,
    x Int,
    y Int
)
ENGINE = MergeTree
ORDER BY (k1, k2)
TTL d + INTERVAL 1 MONTH GROUP BY k1, k2 SET x = max(x), y = min(y);

Удаление просроченных данных

Данные с истекшим TTL удаляются, когда ClickHouse объединяет части данных.

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

Если вы выполняете запрос SELECT между слияниями, вы можете получить просроченные данные. Чтобы этого избежать, используйте запрос OPTIMIZE перед SELECT.

См. также

Типы дисков

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

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

Введение

Семейство движков таблиц 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 для получения дополнительной информации.

Структура конфигурации:

<storage_configuration>
    <disks>
        <disk_name_1> <!-- disk name -->
            <path>/mnt/fast_ssd/clickhouse/</path>
        </disk_name_1>
        <disk_name_2>
            <path>/mnt/hdd1/clickhouse/</path>
            <keep_free_space_bytes>10485760</keep_free_space_bytes>
        </disk_name_2>
        <disk_name_3>
            <path>/mnt/hdd2/clickhouse/</path>
            <keep_free_space_bytes>10485760</keep_free_space_bytes>
        </disk_name_3>

        ...
    </disks>

    ...
</storage_configuration>

Теги:

  • <disk_name_N> — имя диска. Имена должны быть разными для всех дисков.
  • path — путь, по которому сервер будет хранить данные (каталоги data и shadow); должен заканчиваться символом '/'.
  • keep_free_space_bytes — объем свободного дискового пространства, который необходимо зарезервировать.

Порядок определения дисков не имеет значения.

Разметка конфигурации политик хранения:

<storage_configuration>
    ...
    <policies>
        <policy_name_1>
            <volumes>
                <volume_name_1>
                    <disk>disk_name_from_disks_configuration</disk>
                    <max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
                    <load_balancing>round_robin</load_balancing>
                </volume_name_1>
                <volume_name_2>
                    <!-- configuration -->
                </volume_name_2>
                <!-- more volumes -->
            </volumes>
            <move_factor>0.2</move_factor>
        </policy_name_1>
        <policy_name_2>
            <!-- configuration -->
        </policy_name_2>

        <!-- more policies -->
    </policies>
    ...
</storage_configuration>

Теги:

  • 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 (где N — наименьший приоритет) без пропусков.
    • Если все тома помечены, они имеют приоритет в указанном порядке.
    • Если помечены только некоторые тома, те, что без метки, имеют наименьший приоритет и располагаются в порядке, в котором они определены в конфигурации.
    • Если ни один том не помечен, их приоритет соответствует порядку, в котором они объявлены в конфигурации.
    • Два тома не могут иметь одинаковое значение приоритета.

Примеры конфигурации:

<storage_configuration>
    ...
    <policies>
        <hdd_in_order> <!-- policy name -->
            <volumes>
                <single> <!-- volume name -->
                    <disk>disk1</disk>
                    <disk>disk2</disk>
                </single>
            </volumes>
        </hdd_in_order>

        <moving_from_ssd_to_hdd>
            <volumes>
                <hot>
                    <disk>fast_ssd</disk>
                    <max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
                </hot>
                <cold>
                    <disk>disk1</disk>
                </cold>
            </volumes>
            <move_factor>0.2</move_factor>
        </moving_from_ssd_to_hdd>

        <small_jbod_with_external_no_merges>
            <volumes>
                <main>
                    <disk>jbod1</disk>
                </main>
                <external>
                    <disk>external</disk>
                </external>
            </volumes>
        </small_jbod_with_external_no_merges>
    </policies>
    ...
</storage_configuration>

В приведённом примере политика 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. Когда том переполнен, данные переносятся на следующий. Порядок перечисления дисков также важен, поскольку данные записываются на них по очереди.

При создании таблицы к ней можно применить одну из настроенных политик хранения:

CREATE TABLE table_with_non_default_policy (
    EventDate Date,
    OrderID UInt64,
    BannerID UInt64,
    SearchPhrase String
) ENGINE = MergeTree
ORDER BY (OrderID, BannerID)
PARTITION BY toYYYYMM(EventDate)
SETTINGS storage_policy = 'moving_from_ssd_to_hdd'

Политика хранения 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.

Фрагмент конфигурации:

<storage_configuration>
    ...
    <disks>
        <s3>
            <type>s3</type>
            <support_batch_delete>true</support_batch_delete>
            <endpoint>https://clickhouse-public-datasets.s3.amazonaws.com/my-bucket/root-path/</endpoint>
            <access_key_id>your_access_key_id</access_key_id>
            <secret_access_key>your_secret_access_key</secret_access_key>
            <region></region>
            <header>Authorization: Bearer SOME-TOKEN</header>
            <server_side_encryption_customer_key_base64>your_base64_encoded_customer_key</server_side_encryption_customer_key_base64>
            <server_side_encryption_kms_key_id>your_kms_key_id</server_side_encryption_kms_key_id>
            <server_side_encryption_kms_encryption_context>your_kms_encryption_context</server_side_encryption_kms_encryption_context>
            <server_side_encryption_kms_bucket_key_enabled>true</server_side_encryption_kms_bucket_key_enabled>
            <proxy>
                <uri>http://proxy1</uri>
                <uri>http://proxy2</uri>
            </proxy>
            <connect_timeout_ms>10000</connect_timeout_ms>
            <request_timeout_ms>5000</request_timeout_ms>
            <retry_attempts>10</retry_attempts>
            <single_read_retries>4</single_read_retries>
            <min_bytes_for_seek>1000</min_bytes_for_seek>
            <metadata_path>/var/lib/clickhouse/disks/s3/</metadata_path>
            <skip_access_check>false</skip_access_check>
        </s3>
        <s3_cache>
            <type>cache</type>
            <disk>s3</disk>
            <path>/var/lib/clickhouse/disks/s3_cache/</path>
            <max_size>10Gi</max_size>
        </s3_cache>
    </disks>
    ...
</storage_configuration>

См. также настройку вариантов внешних хранилищ.

конфигурация кэша

Версии 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 — Имя диска, на котором хранятся данные.

Статистика по столбцам

Experimental feature. Learn more.
Not supported in ClickHouse Cloud

Объявление статистики задаётся в секции COLUMNS запроса CREATE для таблиц из семейства *MergeTree* при включённой настройке set allow_experimental_statistics = 1.

CREATE TABLE tab
(
    a Int64 STATISTICS(TDigest, Uniq),
    b Float64
)
ENGINE = MergeTree
ORDER BY a

Мы также можем изменять статистику с помощью команд ALTER.

ALTER TABLE tab ADD STATISTICS b TYPE TDigest, Uniq;
ALTER TABLE tab DROP STATISTICS a;

Эта лёгкая статистика агрегирует информацию о распределении значений по столбцам. Статистика хранится в каждой части и обновляется при каждой вставке. Её можно использовать для оптимизации 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 — минимальный размер блоков несжатых данных, необходимый для сжатия при записи следующей метки.

Пример:

CREATE TABLE tab
(
    id Int64,
    document String SETTINGS (min_compress_block_size = 16777216, max_compress_block_size = 16777216)
)
ENGINE = MergeTree
ORDER BY id

Настройки для отдельных столбцов можно изменять или удалять с помощью ALTER MODIFY COLUMN, например:

  • Удалить SETTINGS из определения столбца:
ALTER TABLE tab MODIFY COLUMN document REMOVE SETTINGS;
  • Изменить параметр:
ALTER TABLE tab MODIFY COLUMN document MODIFY SETTING min_compress_block_size = 8192;
  • Сбрасывает одну или несколько настроек и одновременно удаляет объявление настройки в определении столбца в запросе CREATE таблицы.
ALTER TABLE tab MODIFY COLUMN document RESET SETTING min_compress_block_size;