本节摘要:ClickHouse 的副本是最终一致的,不提供强一致。本节讲清它保证什么、不保证什么、写入语义的代价,以及什么时候需要
select_sequential_consistency。
阅读完本节,你应当能够:
ClickHouse 的 ReplicatedMergeTree 保证的是副本最终一致:写入先在一个副本可见,其他副本异步追上,最终所有副本数据一致。它不保证:

ClickHouse 的写入默认是"写入一个副本即返回"。这个副本把 part 落盘、登记到 Keeper 后就告诉客户端成功,其他副本异步追。这意味着:
如果需要"写入多数副本才返回"的更强语义,可以配置 insert_quorum:要求写入至少 N 个副本才返回成功。代价是写入变慢(要等多数副本),且可用性下降(凑不够多数就写不进去)。
| 写入语义 | 配置 | 速度 | 一致性 | 可用性 |
|---|---|---|---|---|
| 写一个副本即返回 | 默认 | 快 | 弱 | 高 |
| 写多数副本才返回 | insert_quorum | 慢 | 强 | 降(需多数存活) |
大多数 OLAP 场景用默认即可——分析容忍短暂不一致,要的是写入吞吐。只有对"刚写必须立刻读到"要求高的场景才上 quorum。
副本 B 落后于副本 A 的程度叫副本延迟。它由几个因素影响:
监控 system.replicas 表的 absolute_delay(落后多少条日志)和 log_pointer:
SELECT database, table, replica_name, absolute_delay, is_readonly, is_session_expired FROM system.replicas;
absolute_delay 持续大说明副本跟不上,要查网络、Keeper 或磁盘。
默认读 Distributed 表可能落到任意副本,刚写的数据可能读不到。如果业务必须"读最新",可以设:
SET select_sequential_consistency = 1;
开启后,查询会确认读的副本已同步到最新日志位置,保证读到已确认的写入。代价是查询变慢(要等副本确认同步位置),且如果副本落后会等或报错。
大多数分析场景不需要强一致读——报表晚几秒看到新数据无所谓。只有"写完立即读验证"类场景才开。
⚠️ 常见坑:有人用 ClickHouse 做"写入后立即查询验证"的流程,结果偶尔查不到刚写的数据,以为是 bug。其实是副本延迟——写入副本 A,查询落到副本 B,B 还没同步。要么写完查同一个副本,要么开 sequential_consistency,要么接受最终一致。
💡 关键直觉:ClickHouse 用"放弃强一致"换"写入吞吐和性能"。理解它保证什么(最终一致)、不保证什么(强一致读、跨分片事务),才能正确设计业务对它的预期。
insert_quorum 写多数才返回(慢、强一致、可用性降)。system.replicas.absolute_delay。select_sequential_consistency = 1,代价是查询变慢,分析场景一般不需要。第 5 章结束。你已经能搭分片副本集群、理解协调服务、判断一致性级别。下一章讲运维和安全,把这些集群管起来。
一致性级别不是抽象的,可以用实验验证。构造一个双副本集群,往一个副本写数据,立刻从另一个副本查询,观察能否读到:
-- 副本 A 写入一条数据 INSERT INTO events_local VALUES ('2024-06-16 10:00:00', 1001, '上海'); -- 立刻从分布式表查询(可能落到任意副本) SELECT count() FROM events_distributed WHERE event_time = '2024-06-16 10:00:00'; -- 查看两个副本的延迟,确认数据是否已同步 SELECT replica_name, absolute_delay FROM system.replicas WHERE table = 'events_local';
如果分布式查询返回 0,而 absolute_delay 大于 0,说明写入了副本 A、查询落到了尚未同步的副本 B——这就是最终一致的典型表现,不是数据丢失。
需要强一点的一致性时,有两条路径。写入侧用 insert_quorum:
-- 要求至少写入 2 个副本才返回成功 INSERT INTO events_local SETTINGS insert_quorum = 2 VALUES ('2024-06-16 10:01:00', 1002, '北京');
读取侧用 select_sequential_consistency:
SET select_sequential_consistency = 1; SELECT count() FROM events_distributed;
insert_quorum 的代价是可用性下降——副本凑不齐多数时写入会失败;select_sequential_consistency 的代价是查询要等副本确认同步位置,变慢。分析场景一般不需要这两者,但当你明确要求"写完立刻能读到"时,知道这两把钥匙放在哪里,比临时抓瞎强得多。