Оптимизация OPTIMIZE для схлопывания данных

1,465 views
Skip to first unread message

Nick

unread,
Aug 14, 2016, 2:43:29 PM8/14/16
to ClickHouse
Здравствуйте!

Пока нет возможности удаления строк, есть следующее кривое решение.

Дано:
— таблица ReplacingMergeTree (пробовался и движок CollapsingMergeTree — результат вроде бы такой же);
— в таблице порядка 1 млрд записей с довольно равномерным распределением по датам за последние годы;
— раз в некоторое редкое время (5-10 минут) происходит вставка порядка 10 тысяч новых записей в таблицу, примерно половина из которых должна заменить предыдущие записи таблицы (Replacing), причём заменяемые записи находятся в совершенно разных местах таблицы (т.е. могут заменяться как давно внесённые записи, так и записи из предыдущей вставки).

Необходимо:
— после вставки убедиться, что дублируемых данных нет (т.е. данные схлопнулись — результат запроса SELECT count() FROM table должен соответствовать SELECT count() FROM table FINAL).

Решение (ну очень кривое :)
Сразу после вставки делается OPTIMIZE table; — но проблема в том, что запрос схлопывает только небольшую часть данных (при первом выполнении буквально пару-тройку строк из тысяч).
Поэтому делаем ещё и ещё OPTIMIZE table;, пока все данные не схлопнутся. 
Признак того, что все строки схлопнулись: запрос OPTIMIZE выполнился за очень малое время (0.001 секунды).

Вопрос: можно ли как-то оптимизировать этот процесс? OPTIMIZE схлопывает с каждым запросом всё большее и большее количество данных, выполняется всё примерно так:
:) optimize table test; -- 0 rows in set. Elapsed: 11.957 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 16.233 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 13.144 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 18.517 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 20.569 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 17.371 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 24.337 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 26.008 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 26.482 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 38.157 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 35.298 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 38.427 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 48.814 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 46.120 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 49.505 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 53.255 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 49.045 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 60.409 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 59.755 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 58.238 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 59.396 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 0.001 sec. 
:) optimize table test; -- 0 rows in set. Elapsed: 0.001 sec. 

Получается очень долго (вся вставка идёт максимум 0.1 секунды, а схлопывание — 10 с лишним минут, впечатление, что кругами переписываются все куски базы по несколько раз вместо того, чтобы переписаться один раз одним OPTIMIZE.

Пробовал поиграться с настройками из https://github.com/yandex/ClickHouse/blob/master/dbms/include/DB/Storages/MergeTree/MergeTreeSettings.h в сторону их увеличения, но никаких изменений не заметил. Может я их неправильно как-то использовал? В config.xml в секции <yandex> просто вставлял типа <max_parts_to_merge_at_once>значение</max_parts_to_merge_at_once>

Спасибо!

man...@gmail.com

unread,
Aug 15, 2016, 4:40:56 PM8/15/16
to ClickHouse
Здравствуйте.

Да, OPTIMIZE делает только очередной шаг слияния.
Есть возможность выполнить слияние всех данных за один раз, для одной партиции.
(Это ещё не вошло в документацию.)

Синтаксис такой:
OPTIMIZE TABLE table PARTITION YYYYMM

Слияние - не очень быстрая операция. Один OPTIMIZE использует только одно процессорное ядро.
Можно выполнить OPTIMIZE нескольких партиций параллельно.

В целом, я не рекомендую делать OPTIMIZE каждый раз после вставки - это очень неоптимально.
Ведь, фактически это означает - перезаписывать ваши миллиарды строк каждый раз заново.

Nikita Zhavoronkov

unread,
Aug 16, 2016, 11:33:59 PM8/16/16
to ClickHouse
Спасибо, да, дело даже не во времени выполнения (которое действительно больше упирается в CPU, а не в I/O), а в том, что на диск всё заново пишется (iostat показывает, что объём записи сопоставим с размером всей таблицы) — тут можно просто диски будет выкидывать через месяц :)

Насчёт партиций идея может быть такая: если задача такова, что заранее точно известно, что какие-то данные будут изменяться (и их — небольшое количество от общего) — их можно класть в отдельную партицию и на неё единственную уже тогда напускать OPTIMIZE.
Reply all
Reply to author
Forward
0 new messages