分布式系统最容易被误解的地方,是大家都喜欢先谈”高可用”,却很少先问一句:我们到底要对什么达成一致?
是对写入顺序达成一致?对某个配置值达成一致?对任务只执行一次达成一致?还是对故障转移后的主节点身份达成一致?
问题问得越泛,方案就越像玄学。问题问得越具体,系统才有机会变得可靠。
一致性不是一个开关
很多人会把一致性想成”强一致”和”最终一致”两个档位,但真实世界没有这么整齐。
至少要拆成几个维度:
- 读写可见性:写入成功后,谁能立刻读到?
- 顺序:并发写入按什么规则排序?
- 复制:数据复制到几个节点才算成功?
- 故障:网络分区、节点宕机、时钟漂移时怎么处理?
- 恢复:故障恢复后,旧节点如何追上新状态?
这几个维度混在一起讨论,就会出现非常熟悉的会议场景:每个人说的都对,但系统还是做不出来。
单点正确很容易,多点正确很贵
在单机系统里,“正确”通常依赖本地事实:内存、磁盘、锁、事务。
到了多节点环境,本地事实会变得不够用。节点 A 认为自己提交成功,节点 B 可能还没收到复制日志;节点 C 以为主节点挂了,其实只是网络慢了一点。
一致性协议的成本,本质上来自这件事:没有任何一个节点天然拥有全局视角。
所以工程上必须引入额外机制:
- 通过日志复制保留顺序。
- 通过多数派确认降低脑裂概率。
- 通过任期编号区分新旧领导者。
- 通过快照和回放减少恢复成本。
这些机制看起来复杂,但它们都在回答同一个问题:当系统里每个节点只看到局部事实时,如何拼出一个足够可信的共同事实。
CAP 的价值在于逼你承认取舍
CAP 经常被讲成口号:一致性、可用性、分区容错只能取其二。
这句话本身没错,但太粗。真正有用的是它逼你承认:网络分区不是异常,而是分布式系统的基本天气。
当网络不可靠时,你必须选择:
- 继续接受请求,但可能读到旧数据。
- 拒绝部分请求,保护写入顺序和状态一致。
没有免费的第三条路。所谓架构判断,就是知道哪些业务能接受短暂不一致,哪些业务宁愿不可用也不能乱写。
消息通知可以最终一致,余额扣减通常不行;配置下发可以延迟,主节点选举不能随缘。技术方案没有脱离业务语义的正确答案。
工程上最怕假装没有失败
一致性设计里最危险的不是复杂,而是假装简单。
比如:
- 写入主库成功就返回,但异步复制失败没有补偿。
- 任务队列说”只执行一次”,实际没有幂等键。
- 多副本缓存各自刷新,最终出现互相覆盖。
- 定时任务多实例部署,却没有租约或抢占机制。
这些问题平时不显眼,因为系统还没遇到真正的并发和故障。一旦遇到,它们就会从”小概率”变成”必现”。
最后
分布式一致性不是为了追求理论洁癖,而是为了让系统在现实世界里少一点自欺欺人。
你不一定需要最强的一致性协议,但你必须知道自己选择了什么:哪里允许延迟,哪里允许重试,哪里必须串行,哪里必须拒绝服务。
把这些问题提前讲清楚,系统会少很多玄学,团队也会少很多凌晨的互相凝视。