主备复制
主备复制由主节点确定写入顺序,再把变化复制到备节点。备节点可以承担读取,也可以在主节点故障后接管。能否安全接管,取决于写入成功时数据已经到哪里,以及备节点是否真正应用了这些变化。
1. 面试核心
先记住这几条,再读正文- 主节点确定写入顺序,备节点按相同日志顺序应用确定的变化,才能得到相同状态。
- 异步复制只在本地确认时,未复制的写入可能随主节点永久故障而丢失。
- 副本收到日志、持久化日志、应用日志是三个不同阶段,确认落盘不代表查询已经能看到结果。卡 · 日志接收与回放
- 新副本先加载一致快照,再追赶快照位置之后的日志,两者必须衔接。
- 安全切换必须保留已确认写入,并隔离旧主的写入能力,确认规则和新主选择规则要配套。卡 · 综合题
2. 写入顺序
主备复制先由一个节点确定顺序,避免副本各自决定更新先后。客户端把写请求交给主节点,主节点分配日志位置或版本,备节点再按顺序应用这些变化。
状态机是按输入更新当前状态的处理逻辑。副本从相同状态出发,按相同顺序处理输入,而且处理结果是确定的,才能得到相同状态。具体系统还要规定谁有写入权、何时返回成功、如何追赶和接管。MySQL、Redis 与共识系统的保证不同,不能仅凭“主备”两个角色判断数据安全。
3. 写入确认时机
返回成功之前等待哪些节点,决定了延迟和数据风险。本地完成就返回,省去了复制等待,但尚未复制的写入会随主节点永久故障而丢失;等待备节点确认可以缩小风险窗口,不过还要问清它确认的是收到、落盘还是已经应用。
多数派共识进一步把确认规则与安全选举连接起来,保证接管者保留已提交数据。等待所有副本则会让最慢或故障副本影响每次写入的完成。
3.1 日志接收与回放
备节点持久化日志后,查询仍可能读到旧值,因为日志还没有应用到可查询的状态。回放就是按日志更新本地状态的过程,它与接收、持久化是不同阶段。
以 PostgreSQL 18 为例,配置同步备库后,synchronous_commit 的取值决定等待到哪一步。这里的 WAL 是数据库用于恢复和复制的预写日志:
| 配置 | 远端确认条件 |
|---|---|
remote_write | 备库已写入操作系统,未要求刷新到持久存储。 |
on | 同步备库已把 WAL 刷到持久存储。 |
remote_apply | 同步备库已回放事务,查询可见其结果。 |
这些远端等待以 synchronous_standby_names 指定了同步备库为前提。remote_write 确认的数据能应对备库进程退出,但操作系统崩溃时仍可能丢失。remote_apply 等得更久,换来的是所等待备库已应用该事务,后续查询可以看到结果。
读取路由若指向其他异步副本,仍需检查它的回放位置。
4. 日志复制
日志复制把每次状态变化按顺序传给备节点,备节点据此更新自己的状态:

如果一次更新依赖当前时间、随机数或外部调用结果,就要把这些值作为明确输入传递。让各副本各自重新取值,即使执行同一段逻辑,也可能得到不同结果。
日志位置用于发现缺口和重复;发现冲突分支后,要按具体协议修复。已经提交的日志前缀,则由提交与切换规则保证不会丢失。
5. 快照
快照保存某个日志位置对应的完整状态。新副本先加载这份状态,就不必从最早的日志重新执行:

快照必须与后续日志衔接。例如,快照包含到某个位置的全部变化,安装后就从该位置之后继续追赶;传输快照期间产生的新日志也必须保留,否则中间会缺一段。
快照解决历史状态传输,日志传播后续变化。采用哪种复制形式,并不能单独决定读取一致性,仍要看确认、读取和切换协议。
6. 故障切换
检测到主节点失联,不代表它已经停止写入。新主接管前,既要确认候选节点包含必须保留的写入,也要通过任期检查或写入隔离(fencing)使旧主失去修改权限。否则,即使数据追平了,仍可能出现两个主节点同时写入。
异步复制无法凭空恢复尚未传播的数据。若目标副本缺失这部分内容,应明确数据损失边界。执行流程见故障转移。
7. 复制延迟
复制延迟可能来自网络传输、磁盘写入或回放速度不足。普通展示如果允许旧值,可以直接读备节点;用户刚写完就要看到结果时,可以读取有效主节点,或等待目标副本应用到这次写入的提交位置。
权限、余额等判断要明确需要多新的数据。监控时同时看日志位置差、时间滞后、积压和追平速度;复制已经停止时,不能继续用上一次采样的低延迟值判断健康。
8. 恢复验证
恢复完成要以数据和业务结果为准。切换后核对新主是否保留已确认请求,客户端缓存和长连接是否仍指向旧主。旧主重新加入前,隔离可能冲突的分支,并按产品恢复流程处理。
消息、搜索和缓存等派生结果通过重放或对账恢复。故障演练需同时验证数据目标和业务恢复时间。
9. 常见问题
9.1 副本越多,就越不会丢数据吗?
不一定。数据安全取决于成功响应前数据保存到了哪里、是否持久化,以及接管时选中了哪个副本。即使副本很多,只要全部落后,最近确认的写入仍可能只存在于主节点。
9.2 等备节点追平自己的写入,就能保证线性一致读吗?
不能只凭这个条件保证。等待备节点应用到自己的写入位置,可以满足读己之写,但没有覆盖之后其他客户端已经完成的更新。线性读取还需要读协议为本次请求取得安全读取位置。
10 · 面试题卡片
本页题目与参考回答
10.1 主节点故障后,怎样保证新主不丢失已提交数据并避免双主
题型:综合题
考察重点
- 写入确认、日志提交与副本追赶。
- 多数派选举、任期和 fencing。
- 快照、故障恢复和数据验证。
参考回答
新主必须保留已确认写入,并隔离旧主的写入能力,才能同时避免数据丢失和双主修改。
写入确认条件必须与新主选择规则配套。只在旧主本地确认的异步复制存在丢失窗口;共识复制则通过安全提交和选举保留已提交前缀。
副本通过快照与日志追平,新主接管前隔离旧写入权。切换后核对已确认请求、旧连接和业务状态,实际保证以所用复制协议为准。
10.2 同步复制已经确认成功,为什么读取备库仍可能得到旧值?
考察重点:接收、持久化、回放与读取路由。
相关内容:日志接收与回放。
参考回答
同步确认可只等待日志持久化,备库尚未回放时仍会读到旧值。
PostgreSQL 18 配置同步备库后,synchronous_commit=on 等待远端 WAL 持久化。synchronous_commit=remote_apply 还等待事务回放。读取其他异步副本时,仍需等待其追平。
11. 参考资料
- Ongaro 与 Ousterhout:In Search of an Understandable Consensus Algorithm
- Liskov 与 Cowling:Viewstamped Replication Revisited
- PostgreSQL 18:synchronous_commit
来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明