复制给每片留出第二、三份,是可用性的命根子;但怎么留、读走哪份、写稳在哪份,又牵动着一致性与性能。本节把主从、多主、链式三种复制策略的读写模型讲透,并给出那句核心心法——"写要稳、读要近、能本地就本地"。
阅读完本节,你应当能够:
前几分钟我们还在谈分片,可如果只分不复制,结果很危险:一片只住在单台节点上,那台机器一断电,这片的数据就当场蒸发。复制解决"数据不能丢、机器不能成单点"的问题——给每片放几份,主坏从顶、读可分流。但副本一起,立刻甩出两个问题:改动怎么在多份之间传播(复制策略),和读写各去谁那儿(读写模型)。
主从复制:写入只进"主"节点,主节点把日志或数据异步/同步传播给各从节点;读取可以分流到主或从。优点:写入路径简单、天然有序、读能水平扩展。软肋:从节点回放大滞后,可能出现"刚写主、读从却读到旧值"的窗口;主挂了要选举新主,短时间不可写。
多主复制:多个主节点都可写,互相传播。优点:就近写入、无单主、容灾强。代价:写写冲突要处理(同一条记录两个主都改),冲突解决策略直接决定一致性品质,处理不好就数据打架。适合跨地域、想"写落最近主"的场景。
链式复制:数据沿固定链路串行传播(主→中→尾)。串行能天然保序,实现简单,可整条链的写可用性取决于最弱一环,某段断了写就停。适合对"序"极敏感、但可接受写入路径集中的场景。
判断一个复制方案好不好,落到一句心法上:写要稳(写入点单一、路径短),读要近(读取尽量落到离客户端近的副本),两者之间用一致性档位来平衡。
下面这张 SVG 展示了读多写少业务里最常见的"写走主、读走从"分流结构,写路径短、读被多台从节点摊平:

读多写少的业务,比如内容聚合、社区动态,主从复制配"读走从"最划算——写成本低、读被多台从节点摊平,这是无数 web 服务的标配。写多读少的业务,比如计数器、日志采集,写路径要尽量短,多主或单主就近写更关键,这时从策略的选择就转向"尽量让别人少排队"。
有个常见误区需要点破:"读走从"不等于"保证读到最新"。从节点数据可能滞后,你让"查余额"也读从,结果用户刚充值就看到旧余额,这就是复制滞后造成的一致性窗口。正因如此,关键读(余额、库存)必须读主或走强一致通道,非关键读(摘要、计数)才放心读从。分清楚这两类读,是复制设计是否专业的标志。
复制滞后是必然的,问题在于你有没有预案。主流做法有三种:一是强一致读——对关键读走"读主或多数副本",牺牲一点延迟换准确;二是因果保持——保证相关操作按因果序渲染(第 2 章讲过);三是明确降级——对不关键的数据,坦然接受短时间内读到旧值,并且在界面上允许用户刷新重试。选哪种取决于"错读会不会惹祸",和"延迟过高用户会不会骂"的权衡。
| 场景 | 读路径 | 写路径 | 一致性代价 |
|---|---|---|---|
| 读多写少·内容 | 读从分散 | 单主短写 | 容忍滞后窗口 |
| 写多读少·计数 | 就近主 | 多主就近写 | 处理写冲突 |
| 关键·账务 | 强一致读主/多数 | 法定多数确认容错 | 延迟更高 |
光说"读走从会读到旧值"是抽象的,做个压测你就懂了。搭一套主从集群,往主节点以每秒 5000 条的速度灌入"账户余额变更"流水,从节点异步同步默认落后约 200 毫秒。此时让监控脚本对余额做"读从",会看到规律性的"回退":读到 100,下一帧又读到 99——因为请求撞上了仍在落后队列里的旧快照。
| 读的类型 | 能不能读从 | 为什么 |
|---|---|---|
| 订单详情、用户主页 | 能 | 摘要/展示类,容忍滞后 |
| 账户余额 | 不能 | 错一分都会投诉 |
| 库存校验 | 不能 | 超卖就出大事 |
| 热搜榜、浏览量 | 能 | 晚几秒无感 |
如果有人把这种"读从"直接接到了提现页,后果就是:用户刚充值到账 100、页面却仍显示 90——钱明明到了,用户却以为被扣了。这不是数据故障,只是读到了滞后的旧副本,但对用户就是实打实的惊吓。
这个实验的教训很直接:别让"关键字段"走异步读从路径。余额、库存这类"错不得"的字段,读主或读多数;热搜榜、浏览数这类"晚几秒无感"的字段,才放心读从。把云读数分配清楚,是复制设计里最简单、也最常被忽略的一课——很多线上"明明没写错、用户却说数据不对"的投诉,根源都是这一处配置没分好。所以奉劝一句:上线前把每一类读的"最迟可几秒旧"写进设计评审表,会替你挡掉绝大多数这类用户投诉。
把三种复制策略和读写模型收拢起来,落到一句选型话上最实用:先看你的读是不是远多于写——是,主从配"读走从"就是最划算,成本低又有得摊平;再看你有没有"多地就近写"的刚需——是,才值得为多主的冲突解决扎扎实实付一笔账,否则单主短写更省心;最后看业务对"先后顺序"敏不敏感——账务流水、证券下单这种必须严格按序回放的,链式复制配强一致读比什么都稳。这三个问题按顺序一问,绝大多数系统挑复制方案时,不用翻资料也能走出一条干净的判断路径,也不至于被某个数据库的营销话术带着走。记住,复制设计从来不是"哪个策略最先进",而是"我的读写形态适合让哪份数据优先、让哪条路径扛压"。
副本有了,可"请求到底怎么被送到正确那块片、送到后要不要顺便合并结果"还是没有着落——下一节 4.3 讲路由与查询代理:它如何按分区键把请求送对门,又如何把多片结果拼成一份完整答案。