复制是把同一份数据放多份的技术手段,一致性模型是"这多份之间对外允诺到什么程度"的语义约定。本节把两者接起来看:主从、多主、链式三种复制拓扑各自能提供哪一档一致性;强一致、顺序一致、因果一致、最终一致四级分别适合什么业务。
复制这件事说起来简单:多放几份,坏一份换一份就完事。可复制完之后,"同一行记录有好几个版本在跑",怎么让用户看起来还是"一张桌子"?这就是一致性模型要回答的问题。它决定了:刚写进去的新值,多久之后所有读请求都能看到;如果有多个写同时发生,先后顺序怎么算。所以复制和一致性根本不是两件事——复制是物理手段,一致性是对外语义约定,我们得把它们放在一起说,才能看清楚拓扑和档位怎么配。
阅读完本节,你应当能够:
复制聊的是"怎么把数据多放几份",一致性模型聊的是"多放了几份之后对外保什么"。很多初学者把两者混为一谈,其实它们一个在物理层、一个在语义层。你可以搭一套主从复制(手段),却并不因此自动获得强一致性(约定)——是否强到"刻读必得新",取决于副本之间同步的究竟是谁、以及读请求往哪份副本走。
主从复制:唯一的"主"节点接受全部写入,其余"从"节点只负责读。写入在主节点完成,日志或数据异步同步到从节点。优点:写入路径简单、天然有序,读能力可以水平扩展。缺点:主节点一旦挂掉,需要选举新主(会有短暂不可用窗口),且异步同步下"读从"可能读到旧值。
多主复制:多个主节点都可以接受写入,节点之间互相复制对方的变化。优点是就近写入、无单点、容灾强。但代价是写写冲突:两个主节点同时改同一条记录,到底以哪个为准,需要冲突解决策略;而不同策略会带来不同的一致性品质,处理不好就是数据打架。
链式复制:数据沿一条确定的链路依次传播,比如主节点传到中间节点、再传到末端。因为传播是串行的,能保证链路上的顺序一致,实现简单,但整条链的可用性取决于最脆弱的环节,一旦某段断了链路就受影响。
下面这张 SVG 用节点与箭头,把三种复制拓扑的写入与同步方向画出来:

复制提供了多份,一致性模型负责给"多份"定契约。按强弱次序排,常见这四档:
强一致(线性一致性):写入一旦提交,任何后续读取(无论去哪个副本)都必然看到这个值。这是最理想的语义,代价是写必须同步到全部(或法定多数)副本才能返回,延迟高、可用性受限。金融账务最喜欢这种。
顺序一致:所有进程看到的是同一个操作顺序,只是这个顺序未必与"墙上的真实时间"一致。它比强一致宽松一档:仍要求全局一个序,但允许与现实时钟有偏差。适合需要一致排序但不苛求即时性的场景。
因果一致:只对有因果关系的操作要求次序——"先发生的必须排在因果相关操作之前",无因果关系的操作可以任意乱序。它非常贴合人类直觉:你发了条朋友圈(A),别人回你(B),那么看到 B 的人一定也看到过 A;但两个无关的操作可以不讲次序。社交内容流大量用这一档。
最终一致:不做任何即时序的诺言,只保证"安静之后会收敛"。它是四档里实现最便宜、可用性最高的一档。
不同复制拓扑配合不同一致性档位,有天生的默契也有别扭。把常见配法列出来,你一看就懂为什么这么配:
| 拓扑 | 最常用档位 | 为什么这么配 |
|---|---|---|
| 主从复制 | 强一致(读主)/最终一致(读从) | 写入只走主,主一致了才放读从,档位天生分层 |
| 多主复制 | 因果一致/最终一致 | 多主同时写,冲突必然存在,只能降档让应用兜底 |
| 链式复制 | 顺序一致 | 传播天然有序,做顺序一致几乎不额外花钱 |
这张配法表的潜台词是:不是拓扑定了就只能配一个档位,但顺着拓扑的天然特性配档位,实现简单、代价又低;非要反着配(多主配强一致),也不是做不到,但复杂度和代价会飙升,不到万不得已别碰。
判断业务处在哪档,通常绕着一个问题打转:"用户刚写的东西,下一秒是不是必须能被任何人读到?"
记住一句工程心法:复制拓扑决定"在哪一层能保一致",一致性档位决定"业务答应给客户怎样的顺序",两者要配着选。配错了的典型惨案是:你选了多主复制、又想让业务享受强一致——结果写冲突加上异步副本,强一致根本兑现不了,冲突解决策略还让数据偶尔打架。
我见过一个比较典型的反面案例:一个社交平台为了追求多主就近写入,用了多主复制,又要求给用户发消息必须"发完立刻能在另一端看到"——强一致要求。结果上线后写冲突频发,系统为了保证强一致频繁做冲突回滚,用户发十条消息退回去三条,体验非常糟糕。其实拆解下来:发消息本身是典型的因果一致场景,只要对方看到消息前一定看到你的对话,不需要立刻强一致;或者如果非要强一致,就该用主从,让所有写入都走同一个主。拓扑和档位配反了,本来可以很顺滑的体验,做成了每天救火的战场。这个教训告诉我们:选型先看业务要的档位,再找对应拓扑,别先把拓扑定死,再硬套档位。
逻辑层的字典已经备齐:CAP 取舍、BASE 妥协、分布策略、复制与一致性。接下来该进入物理层了——第 3 章看看这些节点实际怎么被装进一台真集群:Shared-Nothing、Shared-Disk、Shared-Memory。