Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

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

Этот движок отличается от MergeTree тем, что удаляет повторяющиеся записи с одинаковым значением ключа сортировки (раздел таблицы ORDER BY, а не PRIMARY KEY).

Дедупликация данных происходит только во время слияния. Слияние выполняется в фоновом режиме в непредсказуемый момент, поэтому заранее рассчитывать на него нельзя. Часть данных может остаться необработанной. Хотя вы можете запустить внеплановое слияние с помощью запроса OPTIMIZE, полагаться на это не стоит, поскольку запрос OPTIMIZE считывает и записывает большой объем данных.

Таким образом, ReplacingMergeTree подходит для фонового удаления дубликатов ради экономии места, но не гарантирует полного отсутствия дубликатов.

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

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE = ReplacingMergeTree([ver [, is_deleted]])
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]

Описание параметров запроса см. в разделе описание оператора.

Параметры ReplacingMergeTree

ver

ver — столбец с номером версии. Тип: UInt*, Date, DateTime или DateTime64. Необязательный параметр.

При слиянии ReplacingMergeTree из всех строк с одинаковым ключом сортировки оставляет только одну:

  • Последнюю в выборке, если ver не задан. Выборка — это набор строк из набора частей, участвующих в слиянии. Последней в выборке будет строка из самой недавно созданной части (то есть из последней вставки). Таким образом, после дедупликации для каждого уникального ключа сортировки останется самая последняя строка из самой недавней вставки.
  • С максимальной версией, если указан ver. Если ver одинаков для нескольких строк, то для них будет применяться правило «если ver не задан», то есть останется строка, вставленная последней.

Пример:

-- без ver - побеждает последняя вставленная строка
CREATE TABLE myFirstReplacingMT
(
    `key` Int64,
    `someCol` String,
    `eventTime` DateTime
)
ENGINE = ReplacingMergeTree
ORDER BY key;

INSERT INTO myFirstReplacingMT Values (1, 'first', '2020-01-01 01:01:01');
INSERT INTO myFirstReplacingMT Values (1, 'second', '2020-01-01 00:00:00');

SELECT * FROM myFirstReplacingMT FINAL;

is_deleted

is_deleted — имя столбца, используемого при слиянии для определения того, представляет ли данные в этой строке состояние или строка должна быть удалена; 1 — это строка "удалена", 0 — строка "состояние".

Тип данных столбца — UInt8.

Пример:

-- с ver и is_deleted
CREATE OR REPLACE TABLE myThirdReplacingMT
(
    `key` Int64,
    `someCol` String,
    `eventTime` DateTime,
    `is_deleted` UInt8
)
ENGINE = ReplacingMergeTree(eventTime, is_deleted)
ORDER BY key
SETTINGS allow_experimental_replacing_merge_with_cleanup = 1;

INSERT INTO myThirdReplacingMT Values (1, 'first', '2020-01-01 01:01:01', 0);
INSERT INTO myThirdReplacingMT Values (1, 'first', '2020-01-01 01:01:01', 1);

select * from myThirdReplacingMT final;

0 rows in set. Elapsed: 0.003 sec.

-- удалить строки с is_deleted
OPTIMIZE TABLE myThirdReplacingMT FINAL CLEANUP;

INSERT INTO myThirdReplacingMT Values (1, 'first', '2020-01-01 00:00:00', 0);

select * from myThirdReplacingMT final;

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

При создании таблицы ReplacingMergeTree требуются те же секции, что и при создании таблицы MergeTree.

Устаревший метод создания таблицы
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE [=] ReplacingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [ver])

Все параметры, кроме ver, имеют тот же смысл, что и в MergeTree.

  • ver — столбец с версией. Необязательный параметр. Описание см. в тексте выше.

Дедупликация во время выполнения запроса & FINAL

Во время слияния ReplacingMergeTree выявляет повторяющиеся строки, используя значения столбцов ORDER BY (применяемых при создании таблицы) как уникальный идентификатор, и сохраняет только строку с наибольшей версией. Однако это обеспечивает лишь отложенную корректность: нет гарантии, что строки будут дедуплицированы, поэтому полагаться на это не следует. В результате запросы могут возвращать неверные результаты, поскольку при их выполнении могут учитываться строки обновления и удаления.

Чтобы получать корректные результаты, пользователям нужно дополнять фоновые слияния дедупликацией во время выполнения запроса и исключением удалённых строк. Это можно сделать с помощью оператора FINAL. Например, рассмотрим следующий пример:

CREATE TABLE rmt_example
(
    `number` UInt16
)
ENGINE = ReplacingMergeTree
ORDER BY number

INSERT INTO rmt_example SELECT floor(randUniform(0, 100)) AS number
FROM numbers(1000000000)

0 rows in set. Elapsed: 19.958 sec. Processed 1.00 billion rows, 8.00 GB (50.11 million rows/s., 400.84 MB/s.)

Запрос без FINAL возвращает неверное число (точный результат зависит от слияний):

SELECT count()
FROM rmt_example

При добавлении FINAL получается правильный результат:

SELECT count()
FROM rmt_example
FINAL

Более подробную информацию о FINAL, в том числе о том, как оптимизировать его производительность, см. в нашем подробном руководстве по ReplacingMergeTree.

Navigation