Случай 1: INSERT в одну партицию одной таблицы семейства MergeTree*
Это транзакционная операция (ACID), если вставляемые строки упакованы и вставляются как один блок (см. примечания):
- Атомарность: INSERT либо выполняется целиком, либо целиком отклоняется: если клиенту отправлено подтверждение, значит, были вставлены все строки; если клиенту отправлена ошибка, значит, не была вставлена ни одна строка.
- Согласованность: если ограничения таблицы не нарушены, то все строки из INSERT вставляются и INSERT завершается успешно; если ограничения нарушены, то не вставляется ни одна строка.
- Изоляция: параллельные клиенты видят согласованный снимок таблицы — состояние таблицы либо до попытки INSERT, либо после успешного INSERT; никакое частичное состояние не наблюдается. Клиенты внутри другой транзакции имеют изоляцию снимков, тогда как клиенты вне транзакции имеют уровень изоляции read uncommitted.
- Надёжность: успешный INSERT записывается в файловую систему до отправки ответа клиенту, на одной реплике или нескольких репликах (управляется настройкой
insert_quorum), и ClickHouse может потребовать от ОС синхронизировать данные файловой системы с носителем данных (управляется настройкойfsync_after_insert). - INSERT в несколько таблиц одним оператором возможен, если задействованы materialized view (INSERT со стороны клиента выполняется в таблицу, с которой связаны materialized view).
Случай 2: INSERT в несколько партиций одной таблицы семейства MergeTree*
То же, что и в случае 1 выше, со следующей особенностью:
- Если у таблицы много партиций и INSERT охватывает много партиций, то вставка в каждую партицию выполняется как отдельная транзакция
Случай 3: INSERT в одну distributed таблицу семейства MergeTree*
То же, что и в случае 1 выше, но с одной оговоркой:
- INSERT в таблицу Distributed в целом не является транзакционным, тогда как вставка в каждый сегмент транзакционна
Случай 4: Использование таблицы Buffer
- вставка в таблицы Buffer не обладает ни атомарностью, ни изолированностью, ни согласованностью, ни долговечностью
Случай 5: Использование async_insert
То же, что и в случае 1 выше, со следующим уточнением:
- атомарность обеспечивается, даже если
async_insertвключен и дляwait_for_async_insertустановлено значение 1 (по умолчанию), но если дляwait_for_async_insertустановлено значение 0, то атомарность не гарантируется.
Примечания
- строки, вставляемые клиентом в некотором формате данных, упаковываются в один блок, если:
- формат вставки является построчным (например, CSV, TSV, Values, JSONEachRow и т. д.), а данные содержат менее
max_insert_block_sizeстрок (~1 000 000 по умолчанию) или менееmin_chunk_bytes_for_parallel_parsingбайт (10 MB по умолчанию), если используется параллельный парсинг (он включен по умолчанию) - формат вставки является постолбцовым (например, Native, Parquet, ORC и т. д.), а данные содержат только один блок данных
- формат вставки является построчным (например, CSV, TSV, Values, JSONEachRow и т. д.), а данные содержат менее
- размер вставленного блока в общем случае может зависеть от многих настроек (например:
max_block_size,max_insert_block_size,min_insert_block_size_rows,min_insert_block_size_bytes,preferred_block_size_bytesи т. д.) - если клиент не получил ответ от сервера, он не знает, завершилась ли транзакция успешно, и может повторить транзакцию, используя свойства exactly-once для вставки
- ClickHouse внутренне использует MVCC с изоляцией снимков для параллельных транзакций
- все свойства ACID сохраняются даже в случае принудительного завершения работы или сбоя сервера
- для обеспечения надежности вставки в типичной конфигурации должен быть включен либо insert_quorum между разными AZ, либо fsync
- «consistency» в терминах ACID не охватывает семантику распределенных систем, см. https://jepsen.io/consistency; она регулируется другими настройками (
select_sequential_consistency) - это объяснение не охватывает новую возможность транзакций, которая позволяет использовать полнофункциональные транзакции для нескольких таблиц, materialized view, нескольких SELECT и т. д. (см. следующий раздел о Transactions, Commit, and Rollback)
Транзакции, фиксация и откат
Помимо функциональности, описанной в начале этого документа, ClickHouse экспериментально поддерживает транзакции, фиксацию и откат.
Требования
- Разверните ClickHouse Keeper или ZooKeeper для отслеживания транзакций
- Только БД с движком Atomic (по умолчанию)
- Только движок таблицы MergeTree без репликации
- Включите экспериментальную поддержку транзакций, добавив этот параметр в
config.d/transactions.xml:<clickhouse> <allow_experimental_transactions>1</allow_experimental_transactions> </clickhouse>
Примечания
- Это экспериментальная возможность, и в ней возможны изменения.
- Если во время транзакции возникает исключение, зафиксировать транзакцию нельзя. Это относится ко всем исключениям, включая исключения
UNKNOWN_FUNCTION, вызванные опечатками. - Вложенные транзакции не поддерживаются; вместо этого завершите текущую транзакцию и начните новую
Конфигурация
В этих примерах используется одноузловой сервер ClickHouse с включенным ClickHouse Keeper.
Включите экспериментальную поддержку транзакций
<clickhouse>
<allow_experimental_transactions>1</allow_experimental_transactions>
</clickhouse>Базовая конфигурация для одного узла сервера ClickHouse с включенным ClickHouse Keeper
<clickhouse replace="true">
<logger>
<level>debug</level>
<log>/var/log/clickhouse-server/clickhouse-server.log</log>
<errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
<size>1000M</size>
<count>3</count>
</logger>
<display_name>node 1</display_name>
<listen_host>0.0.0.0</listen_host>
<http_port>8123</http_port>
<tcp_port>9000</tcp_port>
<zookeeper>
<node>
<host>clickhouse-01</host>
<port>9181</port>
</node>
</zookeeper>
<keeper_server>
<tcp_port>9181</tcp_port>
<server_id>1</server_id>
<log_storage_path>/var/lib/clickhouse/coordination/log</log_storage_path>
<snapshot_storage_path>/var/lib/clickhouse/coordination/snapshots</snapshot_storage_path>
<coordination_settings>
<operation_timeout_ms>10000</operation_timeout_ms>
<session_timeout_ms>30000</session_timeout_ms>
<raft_logs_level>information</raft_logs_level>
</coordination_settings>
<raft_configuration>
<server>
<id>1</id>
<hostname>clickhouse-keeper-01</hostname>
<port>9234</port>
</server>
</raft_configuration>
</keeper_server>
</clickhouse>Пример
Убедитесь, что экспериментальные транзакции включены
Выполните BEGIN TRANSACTION или START TRANSACTION, а затем ROLLBACK, чтобы убедиться, что экспериментальные транзакции включены и что ClickHouse Keeper тоже включен, поскольку он используется для отслеживания транзакций.
BEGIN TRANSACTIONOk.ROLLBACKOk.Создайте таблицу для тестирования
CREATE TABLE mergetree_table
(
`n` Int64
)
ENGINE = MergeTree
ORDER BY nOk.Начните транзакцию и вставьте строку
BEGIN TRANSACTIONOk.INSERT INTO mergetree_table FORMAT Values (10)Ok.SELECT *
FROM mergetree_table┌──n─┐
│ 10 │
└────┘Откатите транзакцию и снова выполните запрос к таблице
Убедитесь, что откат транзакции выполнен:
ROLLBACKOk.SELECT *
FROM mergetree_tableOk.
0 rows in set. Elapsed: 0.002 sec.Завершите транзакцию и повторно выполните запрос к таблице
BEGIN TRANSACTIONOk.INSERT INTO mergetree_table FORMAT Values (42)Ok.COMMITOk. Elapsed: 0.002 sec.SELECT *
FROM mergetree_table┌──n─┐
│ 42 │
└────┘Просмотр транзакций
Проверить транзакции можно, выполнив запрос к таблице system.transactions, но учтите, что эту
таблицу нельзя запрашивать из сеанса, в котором открыта транзакция. Откройте второй сеанс клиент ClickHouse, чтобы выполнить запрос к этой таблице.
SELECT *
FROM system.transactions
FORMAT VerticalRow 1:
──────
tid: (33,61,'51e60bce-6b82-4732-9e1d-b40705ae9ab8')
tid_hash: 11240433987908122467
elapsed: 210.017820947
is_readonly: 1
state: RUNNINGБолее подробная информация
См. эту мета-задачу, чтобы ознакомиться с гораздо более обширными тестами и следить за ходом работ.