5.3 数据一致性保障


5.3 数据一致性保障

本节摘要:ClickHouse 的副本是最终一致的,不提供强一致。本节讲清它保证什么、不保证什么、写入语义的代价,以及什么时候需要 select_sequential_consistency

本节导航

阅读完本节,你应当能够:

  1. 说清 ClickHouse 副本的一致性级别
  2. 区分写入语义(写入一个副本 vs 写入多数副本)
  3. 理解副本延迟的影响
  4. 决定何时启用强一致读

一、保证什么,不保证什么

ClickHouse 的 ReplicatedMergeTree 保证的是副本最终一致:写入先在一个副本可见,其他副本异步追上,最终所有副本数据一致。它不保证:

  • 跨副本强一致读:刚写入的数据,从另一个副本读可能读不到(那个副本还没同步完)。
  • 跨分片强事务:没有跨分片事务,分片间各自独立。
  • 写入立即可见:写入副本后,part 要落盘并登记到 Keeper 才可见,有微小延迟。

图 5-3 副本一致性时序

图 5-3 副本一致性时序

二、写入语义

ClickHouse 的写入默认是"写入一个副本即返回"。这个副本把 part 落盘、登记到 Keeper 后就告诉客户端成功,其他副本异步追。这意味着:

  • 写入快(不等所有副本)
  • 但写入返回时,只有那个副本有数据,其他副本还没

如果需要"写入多数副本才返回"的更强语义,可以配置 insert_quorum:要求写入至少 N 个副本才返回成功。代价是写入变慢(要等多数副本),且可用性下降(凑不够多数就写不进去)。

写入语义 配置 速度 一致性 可用性
写一个副本即返回 默认
写多数副本才返回 insert_quorum 降(需多数存活)

大多数 OLAP 场景用默认即可——分析容忍短暂不一致,要的是写入吞吐。只有对"刚写必须立刻读到"要求高的场景才上 quorum。

三、副本延迟

副本 B 落后于副本 A 的程度叫副本延迟。它由几个因素影响:

  • 网络带宽:part 大、网络窄则同步慢
  • part 数量:小 part 多则同步项多
  • Keeper 负载:协调服务慢则通知慢

监控 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,代价是查询变慢,分析场景一般不需要。
  • 设计业务预期时接受最终一致,强一致需求场景慎用 ClickHouse 做主存。

第 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 的代价是查询要等副本确认同步位置,变慢。分析场景一般不需要这两者,但当你明确要求"写完立刻能读到"时,知道这两把钥匙放在哪里,比临时抓瞎强得多。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U