Проблемы с вставкой в Clickhouse


Clickhouse одна из самых крутых вещёй, с которыми я сталкивался в IT. Невероятно быстрая OLAP база данных, которая отлично работает как в облаке (есть решения через AWS), так и локально в составе одного единственного бинарника, который можно легко запустить из CLI.

Но сейчас будет заметка о проблемной части - а именно потенциальных потерях данных при вставке.

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

  • Имеем таблицу A и B
  • Производим вставку в таблицу A новых данных.
  • Сразу же после окончания INSERTа производим вставку в B данных, которая производится с JOIN операцией с данными:
INSERT INTO
    db.B
SELECT 
    *
FROM 
    db.some_source t
INNER JOIN
    db.A ON t.field = db.A.field

После этих действий можно с удивлением обраружить, что некотрой части данных в B не хватает. Или хватает - предсказать довольно сложно.

Приходилось обращаться в поддержку Clickhouse с вопросом, почему такое может происходить, и 100% точного ответа получено не было, но предположительно источниками проблемы могу быть:

  • async_insert: настройка в Clickhouse которая позволяет ему накапливать данные в буфере перед вставкой (бд проще вставлять данные партиями, поскольку она создаёт отдельный файл для этой партии перед мерджем). На момент обращения к следующей таблице данные всё еще могут оставаться в этом буфере.
  • deduplication механизм который позволяет Кликхаусу улавливать дублирующиеся блоки данных и удалять их. По-видимому это несовершенная система, поскольу иногда удаляются данные которые на самом деле не являются дублями. Clickhouse решает это на основе первичного ключа таблицы и в зависимости от её движка. Происходит это или нет можно узнать из системной таблицы system.query_log (колонка ProfileEvents должна содержать информацию о DuplicatedInsertedBlocks).

На самом деле 2 настройки, которые мы добавили к проблемным запросом на текущий момент почти решили наши проблемы с вставкой данных (на самом деле мы ещё включили небольшой делей при серийной вставке данных).

-- Тело запроса
SETTINGS 
   insert_deduplicate = 0, 
   wait_for_async_insert = 1

Мне не приходилось наблюдать такой же проблемы с использованием MATERIALIZED VIEW, которые в Clickhouse позволяют делать автоматическую вставку на постоянной основе - но зачастую нам хочется контроля за ситуацией. С MATERIALIZED VIEW если её по какой-то причине нельзя обновить (например, дропнулась таблица с которой был JOIN), она блокирует вставку в изначальный источник данных, а это, мягко говоря, неприятно (представьте терять получаемые в реальном времени сотни тысяч сторок данных).

В любом случае, надеюсь если кто-то ещё столкнётся с похожей проблемой, эта инфомация поможет её решить.