Caso 1: INSERT em uma partição, de uma tabela, da família MergeTree*
Isso é transacional (ACID) se as linhas inseridas estiverem agrupadas e forem inseridas como um único bloco (consulte as Notas):
- Atomicidade: um INSERT é concluído com êxito ou rejeitado por completo: se uma confirmação for enviada ao cliente, então todas as linhas foram inseridas; se um erro for enviado ao cliente, então nenhuma linha foi inserida.
- Consistência: se nenhuma restrição da tabela for violada, então todas as linhas de um INSERT serão inseridas e o INSERT será concluído com êxito; se as restrições forem violadas, então nenhuma linha será inserida.
- Isolamento: clientes concorrentes observam um snapshot consistente da tabela — o estado da tabela como era antes da tentativa de INSERT ou após o INSERT bem-sucedido; nenhum estado parcial é visto. Clientes dentro de outra transação têm isolamento por snapshot, enquanto clientes fora de uma transação têm nível de isolamento read uncommitted.
- Durabilidade: um INSERT bem-sucedido é gravado no sistema de arquivos antes de responder ao cliente, em uma única réplica ou em várias réplicas (controlado pela configuração
insert_quorum), e o ClickHouse pode solicitar ao SO que sincronize os dados do sistema de arquivos na mídia de armazenamento (controlado pela configuraçãofsync_after_insert). - INSERT em várias tabelas com uma única instrução é possível se houver visões materializadas envolvidas (o INSERT do cliente é feito em uma tabela que tem visões materializadas associadas).
Caso 2: INSERT em múltiplas partições de uma tabela da família MergeTree*
O mesmo que o Caso 1 acima, com este detalhe:
- Se a tabela tiver muitas partições e o INSERT abranger muitas delas, então a inserção em cada partição será transacional por si só
Caso 3: INSERT em uma tabela distribuída da família MergeTree*
O mesmo que o Caso 1 acima, com este detalhe:
- O INSERT em uma tabela distribuída não é transacional como um todo, enquanto a inserção em cada shard é transacional
Caso 4: Usando uma tabela Buffer
- inserções em tabelas Buffer não são atômicas, isoladas, consistentes nem duráveis
Caso 5: Usando async_insert
O mesmo que o Caso 1 acima, com este detalhe:
- a atomicidade é garantida mesmo que
async_insertesteja habilitado ewait_for_async_insertesteja definido como 1 (o padrão), mas, sewait_for_async_insertestiver definido como 0, a atomicidade não é garantida.
Notas
- as linhas inseridas pelo cliente em algum formato de dados são empacotadas em um único bloco quando:
- o formato de inserção é baseado em linhas (como CSV, TSV, Values, JSONEachRow etc.) e os dados contêm menos de
max_insert_block_sizelinhas (~1 000 000 por padrão) ou menos demin_chunk_bytes_for_parallel_parsingbytes (10 MB por padrão), caso o parsing paralelo esteja em uso (habilitado por padrão) - o formato de inserção é baseado em colunas (como Native, Parquet, ORC etc.) e os dados contêm apenas um bloco de dados
- o formato de inserção é baseado em linhas (como CSV, TSV, Values, JSONEachRow etc.) e os dados contêm menos de
- o tamanho do bloco inserido, em geral, pode depender de muitas configurações (por exemplo:
max_block_size,max_insert_block_size,min_insert_block_size_rows,min_insert_block_size_bytes,preferred_block_size_bytesetc.) - se o cliente não recebeu uma resposta do servidor, ele não sabe se a transação foi bem-sucedida e pode repeti-la usando as propriedades de inserção exactly-once
- o ClickHouse usa MVCC com isolamento por snapshot internamente para transações concorrentes
- todas as propriedades ACID continuam válidas mesmo em caso de encerramento/falha do servidor
insert_quorumem diferentes AZs oufsyncdeve estar habilitado para garantir inserts duráveis em uma configuração típica- a "consistência" nos termos ACID não cobre a semântica de sistemas distribuídos; veja https://jepsen.io/consistency, que é controlada por diferentes configurações (
select_sequential_consistency) - esta explicação não cobre um novo recurso de transações que permite ter transações completas em várias tabelas, visões materializadas, para múltiplos SELECTs etc. (veja a próxima seção sobre Transactions, Commit, and Rollback)
Transações, Commit e Rollback
Além da funcionalidade descrita no início deste documento, o ClickHouse oferece suporte experimental a transações, commit e rollback.
Requisitos
- Implante o ClickHouse Keeper ou o ZooKeeper para rastrear transações
- Somente DB atomic (padrão)
- Somente o mecanismo de tabela MergeTree não replicado
- Ative o suporte experimental a transações adicionando esta configuração em
config.d/transactions.xml:<clickhouse> <allow_experimental_transactions>1</allow_experimental_transactions> </clickhouse>
Observações
- Este é um recurso experimental, e é esperado que haja mudanças.
- Se ocorrer uma exceção durante uma transação, você não poderá confirmar a transação. Isso inclui todas as exceções, inclusive exceções
UNKNOWN_FUNCTIONcausadas por erros de digitação. - Transações aninhadas não são compatíveis; finalize a transação atual e inicie uma nova
Configuração
Estes exemplos consideram um servidor ClickHouse de nó único com o ClickHouse Keeper habilitado.
Ative o suporte experimental a transações
<clickhouse>
<allow_experimental_transactions>1</allow_experimental_transactions>
</clickhouse>Configuração básica para um único nó do servidor ClickHouse com o ClickHouse Keeper habilitado
<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>Exemplo
Verifique se as transações experimentais estão habilitadas
Execute um BEGIN TRANSACTION ou START TRANSACTION, seguido de um ROLLBACK, para verificar se as transações experimentais estão habilitadas e se o ClickHouse Keeper também está habilitado, já que ele é usado para rastrear transações.
BEGIN TRANSACTIONOk.ROLLBACKOk.Crie uma tabela para teste
CREATE TABLE mergetree_table
(
`n` Int64
)
ENGINE = MergeTree
ORDER BY nOk.Inicie uma transação e insira uma linha
BEGIN TRANSACTIONOk.INSERT INTO mergetree_table FORMAT Values (10)Ok.SELECT *
FROM mergetree_table┌──n─┐
│ 10 │
└────┘Desfaça a transação e consulte a tabela novamente
Verifique se a transação foi desfeita:
ROLLBACKOk.SELECT *
FROM mergetree_tableOk.
0 rows in set. Elapsed: 0.002 sec.Conclua uma transação e consulte a tabela novamente
BEGIN TRANSACTIONOk.INSERT INTO mergetree_table FORMAT Values (42)Ok.COMMITOk. Elapsed: 0.002 sec.SELECT *
FROM mergetree_table┌──n─┐
│ 42 │
└────┘Introspecção de transações
Você pode inspecionar as transações consultando a tabela system.transactions, mas observe que não é possível consultar essa
tabela em uma sessão que esteja em uma transação. Abra uma segunda sessão do clickhouse client para consultar essa tabela.
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: RUNNINGMais detalhes
Consulte esta meta issue para ver testes muito mais abrangentes e acompanhar o andamento.