> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-8c05c8a2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Page describing transactional (ACID) support in ClickHouse

# Transactional (ACID) support

export const CloudNotSupportedBadge = () => {
  return <div className="cloudNotSupportedBadge">
            <div className="cloudNotSupportedIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.5" d="M6.33366 12.6666L12.3739 12.6667C13.6593 12.6667 14.7073 11.6187 14.7073 10.3334C14.7073 9.04804 13.6593 8.00003 12.3739 8.00003C12.3739 8.00003 12.3337 7.66659 12.0003 7.33325M10.667 5.33322C8.00033 2.33325 4.45395 4.78537 4.14195 6.68203C2.55728 6.7627 1.29395 8.06203 1.29395 9.6667C1.29395 11.3234 2.66699 12.6666 4.00033 12.6666" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.5" d="M2.66699 14L12.0003 4.66663" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>

        </div>
            Not supported in ClickHouse Cloud
        </div>;
};

export const ExperimentalBadge = () => {
  return <div className="experimentalBadge">
            <div className="experimentalIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.25" d="M5.5 2H10.5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M9.50015 2V6.19625L13.4283 12.7425C13.4738 12.8183 13.4985 12.9049 13.4996 12.9934C13.5008 13.0818 13.4785 13.169 13.435 13.246C13.3914 13.323 13.3283 13.3871 13.2519 13.4317C13.1755 13.4764 13.0886 13.4999 13.0002 13.5H3.00015C2.91164 13.5 2.8247 13.4766 2.74822 13.432C2.67174 13.3874 2.60847 13.3233 2.56487 13.2463C2.52126 13.1693 2.49889 13.082 2.50004 12.9935C2.50119 12.905 2.52582 12.8184 2.5714 12.7425L6.50015 6.19625V2" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M4.47656 9.56754C5.30344 9.41254 6.47656 9.47942 7.99969 10.25C10.0153 11.2707 11.4216 11.0569 12.2184 10.7282" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
        </div>
            Experimental feature. <u><a href="/docs/beta-and-experimental-features#experimental-features">Learn more.</a></u>
        </div>;
};

<h2 id="case-1-insert-into-one-partition-of-one-table-of-the-mergetree-family">
  Case 1: INSERT into one partition, of one table, of the MergeTree\* family
</h2>

This is transactional (ACID) if the inserted rows are packed and inserted as a single block (see Notes):

* Atomic: an INSERT succeeds or is rejected as a whole: if a confirmation is sent to the client, then all rows were inserted; if an error is sent to the client, then no rows were inserted.
* Consistent: if there are no table constraints violated, then all rows in an INSERT are inserted and the INSERT succeeds; if constraints are violated, then no rows are inserted.
* Isolated: concurrent clients observe a consistent snapshot of the table–the state of the table either as it was before the INSERT attempt, or after the successful INSERT; no partial state is seen. Clients inside of another transaction have [snapshot isolation](https://en.wikipedia.org/wiki/Snapshot_isolation), while clients outside of a transaction have [read uncommitted](https://en.wikipedia.org/wiki/Isolation_\(database_systems\)#Read_uncommitted) isolation level.
* Durable: a successful INSERT is written to the filesystem before answering to the client, on a single replica or multiple replicas (controlled by the `insert_quorum` setting), and ClickHouse can ask the OS to sync the filesystem data on the storage media (controlled by the `fsync_after_insert` setting).
* INSERT into multiple tables with one statement is possible if materialized views are involved (the INSERT from the client is to a table which has associate materialized views).

<h2 id="case-2-insert-into-multiple-partitions-of-one-table-of-the-mergetree-family">
  Case 2: INSERT into multiple partitions, of one table, of the MergeTree\* family
</h2>

Same as Case 1 above, with this detail:

* If table has many partitions and INSERT covers many partitions, then insertion into every partition is transactional on its own

<h2 id="case-3-insert-into-one-distributed-table-of-the-mergetree-family">
  Case 3: INSERT into one distributed table of the MergeTree\* family
</h2>

Same as Case 1 above, with this detail:

* INSERT into Distributed table is not transactional as a whole, while insertion into every shard is transactional

<h2 id="case-4-using-a-buffer-table">
  Case 4: Using a Buffer table
</h2>

* insert into Buffer tables is neither atomic nor isolated nor consistent nor durable

<h2 id="case-5-using-async_insert">
  Case 5: Using async\_insert
</h2>

Same as Case 1 above, with this detail:

* atomicity is ensured even if `async_insert` is enabled and `wait_for_async_insert` is set to 1 (the default), but if `wait_for_async_insert` is set to 0, then atomicity is not ensured.

<h2 id="notes">
  Notes
</h2>

* rows inserted from the client in some data format are packed into a single block when:
  * the insert format is row-based (like CSV, TSV, Values, JSONEachRow, etc) and the data contains less then `max_insert_block_size` rows (\~1 000 000 by default) or less then `min_chunk_bytes_for_parallel_parsing` bytes (10 MB by default) in case of parallel parsing is used (enabled by default)
  * the insert format is column-based (like Native, Parquet, ORC, etc) and the data contains only one block of data
* the size of the inserted block in general may depend on many settings (for example: `max_block_size`, `max_insert_block_size`, `min_insert_block_size_rows`, `min_insert_block_size_bytes`, `preferred_block_size_bytes`, etc)
* if the client did not receive an answer from the server, the client does not know if the transaction succeeded, and it can repeat the transaction, using exactly-once insertion properties
* ClickHouse is using [MVCC](https://en.wikipedia.org/wiki/Multiversion_concurrency_control) with [snapshot isolation](https://en.wikipedia.org/wiki/Snapshot_isolation) internally for concurrent transactions
* all ACID properties are valid even in the case of server kill/crash
* either insert\_quorum into different AZ or fsync should be enabled to ensure durable inserts in the typical setup
* "consistency" in ACID terms does not cover the semantics of distributed systems, see [https://jepsen.io/consistency](https://jepsen.io/consistency) which is controlled by different settings (select\_sequential\_consistency)
* this explanation does not cover a new transactions feature that allow to have full-featured transactions over multiple tables, materialized views, for multiple SELECTs, etc. (see the next section on Transactions, Commit, and Rollback)

<h2 id="transactions-commit-and-rollback">
  Transactions, Commit, and Rollback
</h2>

In addition to the functionality described at the top of this document, ClickHouse has experimental support for transactions, commits, and rollback functionality.

<h3 id="requirements">
  Requirements
</h3>

* Deploy ClickHouse Keeper or ZooKeeper to track transactions
* Atomic DB only (Default)
* Non-Replicated MergeTree table engine only
* Enable experimental transaction support by adding this setting in `config.d/transactions.xml`:
  ```xml theme={null}
  <clickhouse>
    <allow_experimental_transactions>1</allow_experimental_transactions>
  </clickhouse>
  ```

<h3 id="notes-1">
  Notes
</h3>

* This is an experimental feature, and changes should be expected.
* If an exception occurs during a transaction, you cannot commit the transaction.  This includes all exceptions, including `UNKNOWN_FUNCTION` exceptions caused by typos.
* Nested transactions are not supported; finish the current transaction and start a new one instead

<h3 id="configuration">
  Configuration
</h3>

These examples are with a single node ClickHouse server with ClickHouse Keeper enabled.

<h4 id="enable-experimental-transaction-support">
  Enable experimental transaction support
</h4>

```xml title=/etc/clickhouse-server/config.d/transactions.xml theme={null}
<clickhouse>
    <allow_experimental_transactions>1</allow_experimental_transactions>
</clickhouse>
```

<h4 id="basic-configuration-for-a-single-clickhouse-server-node-with-clickhouse-keeper-enabled">
  Basic configuration for a single ClickHouse server node with ClickHouse Keeper enabled
</h4>

<Note>
  See the [deployment](/guides/oss/deployment-and-scaling/terminology) documentation for details on deploying ClickHouse server and a proper quorum of ClickHouse Keeper nodes.  The configuration shown here is for experimental purposes.
</Note>

```xml title=/etc/clickhouse-server/config.d/config.xml theme={null}
<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>
```

<h3 id="example">
  Example
</h3>

<h4 id="verify-that-experimental-transactions-are-enabled">
  Verify that experimental transactions are enabled
</h4>

Issue a `BEGIN TRANSACTION` or `START TRANSACTION` followed by a `ROLLBACK` to verify that experimental transactions are enabled, and that ClickHouse Keeper is enabled as it is used to track transactions.

```sql theme={null}
BEGIN TRANSACTION
```

```response theme={null}
Ok.
```

<Tip>
  If you see the following error, then check your configuration file to make sure that `allow_experimental_transactions` is set to `1` (or any value other than `0` or `false`).

  ```response theme={null}
  Code: 48. DB::Exception: Received from localhost:9000.
  DB::Exception: Transactions are not supported.
  (NOT_IMPLEMENTED)
  ```

  You can also check ClickHouse Keeper by issuing

  ```bash theme={null}
  echo ruok | nc localhost 9181
  ```

  ClickHouse Keeper should respond with `imok`.
</Tip>

```sql theme={null}
ROLLBACK
```

```response theme={null}
Ok.
```

<h4 id="create-a-table-for-testing">
  Create a table for testing
</h4>

<Tip>
  Creation of tables is not transactional.  Run this DDL query outside of a transaction.
</Tip>

```sql theme={null}
CREATE TABLE mergetree_table
(
    `n` Int64
)
ENGINE = MergeTree
ORDER BY n
```

```response theme={null}
Ok.
```

<h4 id="begin-a-transaction-and-insert-a-row">
  Begin a transaction and insert a row
</h4>

```sql theme={null}
BEGIN TRANSACTION
```

```response theme={null}
Ok.
```

```sql theme={null}
INSERT INTO mergetree_table FORMAT Values (10)
```

```response theme={null}
Ok.
```

```sql theme={null}
SELECT *
FROM mergetree_table
```

```response theme={null}
┌──n─┐
│ 10 │
└────┘
```

<Note>
  You can query the table from within a transaction and see that the row was inserted even though it has not yet been committed.
</Note>

<h4 id="rollback-the-transaction-and-query-the-table-again">
  Rollback the transaction, and query the table again
</h4>

Verify that the transaction is rolled back:

```sql theme={null}
ROLLBACK
```

```response theme={null}
Ok.
```

```sql theme={null}
SELECT *
FROM mergetree_table
```

```response theme={null}
Ok.

0 rows in set. Elapsed: 0.002 sec.
```

<h4 id="complete-a-transaction-and-query-the-table-again">
  Complete a transaction and query the table again
</h4>

```sql theme={null}
BEGIN TRANSACTION
```

```response theme={null}
Ok.
```

```sql theme={null}
INSERT INTO mergetree_table FORMAT Values (42)
```

```response theme={null}
Ok.
```

```sql theme={null}
COMMIT
```

```response theme={null}
Ok. Elapsed: 0.002 sec.
```

```sql theme={null}
SELECT *
FROM mergetree_table
```

```response theme={null}
┌──n─┐
│ 42 │
└────┘
```

<h3 id="transactions-introspection">
  Transactions introspection
</h3>

You can inspect transactions by querying the `system.transactions` table, but note that you cannot query that
table from a session that is in a transaction. Open a second `clickhouse client` session to query that table.

```sql theme={null}
SELECT *
FROM system.transactions
FORMAT Vertical
```

```response theme={null}
Row 1:
──────
tid:         (33,61,'51e60bce-6b82-4732-9e1d-b40705ae9ab8')
tid_hash:    11240433987908122467
elapsed:     210.017820947
is_readonly: 1
state:       RUNNING
```

<h2 id="more-details">
  More Details
</h2>

See this [meta issue](https://github.com/ClickHouse/ClickHouse/issues/48794) to find much more extensive tests and to keep up to date with the progress.
