起因
熟悉我的朋友都知道,我从去年 9 月开始就对分布式系统比较沉迷,被那些协议的设计思想深深吸引。
最近偶然看到一个知识盲区:MySQL 主从复制。看到三种复制方式:同步复制、异步复制、半同步复制。
看到异步复制的时候,我脑子里立刻蹦出来:这不就是 Redis 默认的复制方式吗?快,但是可能丢数据。
看到同步复制的时候,我又想到了 Redis------Redis 也有同步复制,同样会慢,等所有从库都写完才返回,谁受得了。
(这里我脑子里其实还闪过另一个念头:Redis 的同步复制会数据不一致。但我没深究 MySQL 会不会也一样,因为我对半同步更感兴趣,直接打住了。后来才发现,MySQL 的同步复制其实演进过------早期版本是先提交事务再等待确认,确实也会不一致;到了 5.x 版本才调整顺序,变成先等待从库确认再提交事务,这才是真正的同步。)
然后看到半同步复制------介于两者之间的方案。我一看描述:
执行完事务后,不立刻返回,也不等所有从库完成,而是等至少一个从库确认收到事件后,再返回给客户端。
到这里,我的分布式系统DNA动了。
我的思考过程
第一步:主从的目的是什么?
主从架构存在的意义,说白了就是:主挂了,从能上位。
如果没有这个前提,搞主从干嘛?读写分离而已的话,异步复制就够了。
所以主从的核心价值 = 高可用 = 主挂了能切换。
第二步:主挂了怎么切换?
既然要从库上位,那多个从库选谁?
这个问题我虽然忘了具体是 Raft 还是 Paxos 还是哪个协议讲的,但有一个原则我记得很清楚:
选数据最新的那个当主。
为什么?因为数据最新的节点,丢失的事务最少,新主和旧主之间的数据差距最小。
第三步:那半同步复制在干嘛?
半同步复制要求至少一个从库确认收到事件,主库才返回成功。
这不就是在保证:任何时候,至少有一个从库的数据是接近主库的吗?
这样的话,主挂了的时候,至少有一个从库的数据是最新的(或者接近最新),选它当新主,数据丢失最少。
第四步:串起来
所以我的推导链路是这样的:
主从的目的 → 主挂了从能上位
↓
多个从库选谁?→ 选数据最新的
↓
怎么保证有从库数据是最新的?→ 半同步复制
↓
至少一个从库确认收到 → 随时可以选它当新主
我当时就觉得,MySQL 肯定也是这个思路。
验证:我的推测对不对?
去确认了一下,结果如下:
对的部分
- 故障转移时,确实会选择数据最新的从库当新主
- 半同步复制确实是为了减少主库宕机时的数据丢失
- "选最新的"这个原则,不管在 Raft、Paxos 还是 MySQL 里,都是通用的
需要修正的部分
半同步复制本身不负责选主。
它只负责一件事:保证数据至少存在于两个地方(主库 + 至少一个从库)。
选主这件事,是额外的故障转移组件来做的,比如:
- MHA(Master High Availability)
- Orchestrator
- MySQL Group Replication(MGR)
所以准确地说:
| 机制 | 职责 |
|---|---|
| 半同步复制 | 数据保护,防丢数据 |
| 故障转移组件 | 检测主库故障,选最新的从库当新主 |
两者是配合关系,不是同一个东西。
总结:迁移学习是真的有用
说实话,Raft 的具体细节我早就忘了,选举流程、日志复制、心跳机制......大部分都记不清了。
但有一个东西留下来了:选主要选数据最新的。
这个原则变成了我的思维工具,看到 MySQL 半同步复制的时候,自动就调用了出来。
所以我觉得,学一个东西最好的检验方式,不是看你能背多少,而是看它能不能帮你理解另一个东西。