Транзакции в ClickHouse

транзакции ClickHouse, курсы ClickHouse для дата-инженера, инженерия данных примеры курсы обучение, DWH ClickHouse, Школа Больших Данных Учебный Центр Коммерсант

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

Особенности операций вставки в ClickHouse

В ClickHouse нет полноценных транзакций, поскольку это колоночное хранилище в первую очередь ориентировано на чтение большого объема данных, потоковую запись и приблизительные вычисления. Это надо учитывать при выполнении DML-запросов. Поскольку данные в этой колоночной базе хранятся в столбцах, а обрабатываются в их фрагментах —  массивах или векторах, рекомендуется выполняться массовые операции с данными. Такое векторизованное выполнение запросов снижает стоимость фактической обработки данных.

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

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

Из-за того, что данные сперва группируются в пакет, а только потом фактически записываются, нарушается требование к долговечности транзакции. Впрочем, в последней версии ClickHouse добавлена экспериментальная поддержка ACID для операций вставки только для таблиц движка MergeTree без репликации данных. Это возможно, поскольку при использовании табличного движка MergeTree операции вставки сразу в хранилище создают части таблицы, которые объединяются фоновым процессом. В случае синхронной вставки данных в таблицы семейства MergeTree данные быстро записываются в хранилище БД в виде новой части данных синхронно с приемом запроса на вставку. Когда ClickHouse получает запрос на вставку, данные запроса  синхронно, т.е. немедленно записываются в виде хотя бы одной новой части данных ( на каждый ключ разделения в хранилище, после чего ClickHouse подтверждает успешное выполнение INSERT-запроса. Параллельно и в любом порядке ClickHouse может получать и выполнять другие запросы на вставку.

При этом INSERT-запрос сортирует входящие данные по первичному ключу и разбивает их на партиции по ключу партиционирования. Вставка данных в несколько партиций одновременно снижает производительность, поэтому рекомендуется вставлять данные большими пакетами, например, по 100 000 строк и группировать их по ключу партиционирования перед загрузкой в ClickHouse.

Снижения производительности не будет, если данные поступают в режиме реального времени  и они отсортированы по времени. Также можно вставлять данные асинхронно во множественных маленьких вставках. При асинхронной вставке данные сначала вставляются в буфер, а затем записываются в хранилище БД позднее или асинхронно. Включить асинхронный режим поможет настройка async_insert. Но асинхронные вставки поддерживаются только через протокол HTTP, а дедупликация при этом не выполняется.

Как соблюдаются ACID-требования к транзакциям?

Поддержка ACID-требований к транзакциям в ClickHouse пока является экспериментальной и реализована только для INSERT-операций и только для таблиц движка MergeTree без репликации данных. Создание таблиц не является транзакционным. Также не поддерживаются вложенные транзакции: придется завершить текущую транзакцию и затем начать новую.

Если вставка выполняется в один раздел одной таблицы, соблюдается атомарность: INSERT выполняется успешно или отклоняется в целом. Также гарантируется согласованность: если ограничения таблицы не нарушены, то все строки вставляются. Обеспечивается изоляция: разные клиенты параллельно наблюдают согласованный снимок таблицы, т.е. ее конечное состояние таблицы до попытки выполнения операции INSERT или после ее успешного завершения. Наконец, успешная вставка записывается в файловую систему перед ответом клиенту, в одной или нескольких репликах, что контролируется настройкой insert_quorum. При этом ClickHouse может указать ОС синхронизировать данные файловой системы на носителе, что контролируется настройкой fsync_after_insert. Вставка данных в несколько таблиц с помощью одного SQL-оператора возможна при использовании материализованных представлений, т.е. INSERT относится к таблице, с которой связано MATERIALIZED VIEW. Если в таблице объявлены ограничения, их выполнимость проверяется для каждой вставляемой строки. Когда  хотя бы для одной строки ограничения не выполняются, INSERT-запрос будет остановлен.

При вставке данных в несколько разделов одной таблицы вставка в каждый раздел сама по себе является транзакционной. INSERT в распределенную таблицу не является транзакционной в целом, хотя вставка в каждый отдельный сегмент является транзакционной. При вставке данных в буферную таблицу, т.е. использующую движок Buffer Table Engine, эта операция не является транзакционной, т.к. при записи данные буферизуются в оперативной памяти и периодически сбрасывая их в другую таблицу. Во время чтения из буферной таблицы данные считываются из буфера и другой таблицы одновременно. Такие таблицы полезны, когда идет слишком много INSERT-запросов от большого количества серверов, и данные не могут быть буферизованы перед вставкой механизмами других табличных движков.

При асинхронной вставке атомарность обеспечивается, даже если настройка async_insert включена и для параметра wait_for_async_insert установлено значение 1 (по умолчанию). Когда wait_for_async_insert равен 0, атомарность асинхронной вставки не обеспечивается.

Как уже было отмечено ранее, поддержка транзакция пока является экспериментальной функцией, которую надо специально включать в ClickHouse. Для этого надо внести изменения в xml-файл конфигурации /etc/clickhouse-server/config.d/transactions.xml, установив настройку allow_experimental_transactions в значение 1:

<clickhouse>
    <allow_experimental_transactions>1</allow_experimental_transactions>
</clickhouse>

Для отслеживания транзакций используется ClickHouse Keeper — сервис репликации данных и выполнения распределенных DDL-запросов. Он реализует клиент-серверный протокол, совместимый с ZooKeeper. Проверить, что поддержка транзакций включена, позволит  инструкция BEGIN TRANSACTION. Например, в песочнице fiddle.clickhouse.com они не поддерживаются.

Проверка поддержки транзакций в песочнице Clickhouse
Проверка поддержки транзакций в песочнице Clickhouse

При выполнении транзакционной вставки можно обратиться к обрабатываемой таблице и увидеть, что строка была вставлена, даже если она еще не зафиксирована. Проверить транзакции можно, запросив таблицу system.transactions. Однако, это нельзя сделать в рамках сеанса, находящегося в транзакции — придется открыть другой сеанс клиента Clickhouse, чтобы запросить эту таблицу.

В заключение отметим, что мутации, т.е. изменение и удаление данных, никак не блокируют вставки и для MergeTree-таблиц выполняются, перезаписывая данные по частям. При этом атомарности нет — куски заменяются на мутированные по мере выполнения и запрос SELECT, заданный во время выполнения мутации, увидит данные как из измененных кусков, так и из кусков, которые еще не были изменены. Мутации линейно упорядочены между собой и накладываются на каждый кусок в порядке добавления. Мутации также упорядочены со вставками — гарантируется, что данные, вставленные в таблицу до начала выполнения запроса мутации, будут изменены, а данные, вставленные после окончания запроса мутации, изменены не будут. Запрос завершается немедленно после добавления информации о мутации для реплицированных таблиц — в ZooKeeper, для нереплицированных — на файловую систему. Сама мутация выполняется асинхронно, используя настройки системного профиля. Следить за ходом её выполнения можно по таблице system.mutations. Добавленные мутации будут выполняться до конца даже в случае перезапуска серверов ClickHouse. Откатить мутацию после её добавления нельзя, но если мутация по какой-то причине не может выполниться до конца, её можно остановить с помощью запроса KILL MUTATION.

Записи о последних выполненных мутациях удаляются не сразу: количество сохраняемых мутаций определяется параметром движка таблиц finished_mutations_to_keep. Более старые записи удаляются. Для нереплицируемых таблиц, все запросы ALTER выполняются синхронно. Для реплицируемых таблиц, запрос всего лишь добавляет инструкцию по соответствующим действиям в ZooKeeper, а сами действия осуществляются при первой возможности. Но при этом, запрос может ждать завершения выполнения этих действий на всех репликах. Для всех запросов ALTER можно настроить ожидание с помощью настройки alter_sync, указав время ожидания (в секундах) выполнения всех запросов ALTER для неактивных реплик с помощью настройки replication_wait_for_inactive_replica_timeout.

Узнайте больше про применение ClickHouse и Apache NiFi для аналитики больших данных на специализированных курсах в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

Источники

  1. https://clickhouse.com/docs/ru/introduction/distinctive-features
  2. https://clickhouse.com/docs/en/guides/developer/transactional
  3. https://habr.com/ru/articles/705590/
  4. https://clickhouse.com/blog/asynchronous-data-inserts-in-clickhouse
  5. https://clickhouse.com/docs/ru/sql-reference/statements/alter
Я даю свое согласие на обработку персональных данных и соглашаюсь с политикой конфиденциальности.
Поиск по сайту