数据库读写分离
数据库读写分离通过 Master 承担写请求、Slave 承担读请求来扩展高并发读能力,但主从复制会带来主从延迟问题,因此需要根据业务一致性要求选择同步复制、强制读主或会话分离等方案,在读性能、写性能和数据一致性之间做权衡。
一、核心主线
高并发读场景的第一种通用方案:数据库读/写分离。
大多数互联网应用都是读多写少:
1 | 刷帖请求 > 发帖请求 |
因此,数据库承受的高并发压力通常主要来自读请求。
读写分离的核心思路是:
1 | 写请求 → 写库 Master |
这样可以把大量读请求从主库中分流出去,降低数据库访问压力,缩短请求响应时间。
二、读写分离架构
1. 基本结构
读写分离通常基于数据库主从复制实现:
1 | 业务服务器 |
其中:
- Master:数据库主节点,作为写库,处理写请求;
- Slave:数据库从节点,作为读库,处理读请求;
- 主从复制:Master 将写入后的最新数据同步到 Slave。
一个 Master 可以连接多个 Slave,从而扩展读能力。
2. 读写分离的价值
读写分离主要解决两个问题:
- 降低主库压力:读请求不再全部打到 Master;
- 提升读吞吐量:多个 Slave 可以共同承担读请求。
适合场景:
- 读多写少;
- 读请求压力明显高于写请求;
- 可以容忍一定程度的主从同步延迟;
- 业务读写路径比较清晰。
三、读写请求路由方式
读写分离的关键问题是:
谁来判断一个请求应该访问 Master 还是 Slave?
本节介绍了两种常见路由方式。
1. 基于数据库 Proxy 的方式
在业务服务和数据库之间增加数据库代理层:
1 | 业务服务 |
Proxy 会根据 SQL 类型进行路由:
| SQL 类型 | 路由目标 |
|---|---|
insert |
Master |
delete |
Master |
update |
Master |
select |
Slave |
常见实现包括:
- MySQL-Proxy;
- MyCat;
- MySQL-Router。
优点
- 读写分离逻辑集中在 Proxy;
- 业务代码改造较少;
- 对业务服务相对透明;
- 适合统一治理数据库访问。
缺点
- Proxy 本身可能成为性能瓶颈;
- Proxy 需要保证高可用;
- SQL 解析和路由逻辑可能增加复杂度;
- 业务侧对细粒度路由控制能力较弱。
2. 基于应用内嵌的方式
读写路由逻辑直接嵌入业务服务进程中:
1 | 业务服务内部数据库访问框架 |
常见数据库连接框架或组件包括:
- gorm;
- shardingjdbc。
优点
- 少一层网络代理;
- 路由控制更灵活;
- 可以结合业务语义做更精细的读写选择;
- 不会引入中心化 Proxy 瓶颈。
缺点
- 业务服务需要引入读写分离逻辑;
- 多语言、多框架环境下治理成本更高;
- 路由规则分散在应用侧,统一管理难度较大。
四、主从延迟问题
1. 主从延迟是什么
读写分离依赖主从复制,而主从复制通常不是瞬时完成的。
当 Master 写入成功后,Slave 可能还没有同步到最新数据:
1 | 写请求 → Master 写入成功 |
这就是主从延迟。
2. 主从延迟带来的问题
主从延迟会造成短时间内的主从数据不一致。
典型表现:
- 用户刚发布内容,刷新页面却看不到;
- 用户刚修改资料,读取时还是旧资料;
- 用户刚完成操作,下一个查询请求读到了旧状态。
所以,读写分离提升了读性能,但引入了一致性问题。
五、主从延迟的三种解决方案
1. 同步数据复制
原理
默认主从复制通常是异步模式:
1 | Master 写完数据 → 直接返回成功 |
同步复制则要求:
1 | Master 写完数据 |
优点
- Master 和 Slave 能在写操作成功后同时读取到最新数据;
- 主从一致性更强;
- 业务服务改造较少。
缺点
- Master 必须等待全部 Slave;
- 写请求延迟明显增加;
- 数据库吞吐量下降;
- 某个 Slave 慢或异常会影响整体写入。
适用场景
同步复制实用价值较低,更适合:
- 低并发场景;
- 对一致性要求极高;
- 写入吞吐压力不大的业务。
2. 强制读主
原理
不同业务场景对主从延迟的容忍度不同。
对于不能容忍主从延迟的读请求,直接路由到 Master:
1 | 普通读请求 → Slave |
示例
用户 A 刚发布一条状态:
- A 自己刷新个人主页时,应该立刻看到新状态;
- 好友 B 查看 A 的主页时,可以暂时看不到这条新状态。
因此:
1 | A 自己读自己的新状态 → 强制读 Master |
优点
- 针对关键场景保证读到最新数据;
- 不需要所有读请求都访问主库;
- 在一致性和读扩展之间做平衡。
缺点
- 需要识别哪些场景不能容忍延迟;
- 强制读主过多会削弱读写分离效果;
- 业务逻辑复杂度增加。
3. 会话分离
原理
如果某个会话刚执行过写操作,那么在接下来很短的一段时间内,该会话的读请求临时路由到 Master。
1 | 用户会话发生写操作 |
设计关键
强制读主的时间窗口应略高于主从复制延迟。
例如:
1 | 主从复制通常延迟 200 ms |
这样可以尽量覆盖主从数据复制的实际延迟时间。
优点
- 保证“自己写的数据自己立刻可见”;
- 不需要所有用户都强制读主;
- 对主库压力影响相对可控。
缺点
- 需要维护会话状态;
- 需要估算合理的主从延迟时间窗口;
- 如果实际延迟超过窗口,仍可能读到旧数据;
- 实现复杂度高于普通读写分离。
六、三种主从延迟方案对比
| 方案 | 核心思路 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 同步数据复制 | Master 等所有 Slave 收到数据后再返回 | 一致性强,业务改造少 | 写延迟高,吞吐下降 | 低并发、强一致 |
| 强制读主 | 不能容忍延迟的读请求访问 Master | 精准保证关键读一致性 | 需要业务识别场景,读主过多会压垮 Master | 局部强一致场景 |
| 会话分离 | 写后短时间内,同一会话读 Master | 保证用户读到自己的写入 | 需要维护会话和时间窗口 | 写后读一致性场景 |
七、核心架构思想
- 大部分互联网系统是读多写少,数据库压力主要来自读请求。
- 读写分离通过 Master 处理写、Slave 处理读来扩展读能力。
- 读写分离通常依赖数据库主从复制。
- 读写路由可以放在数据库 Proxy,也可以放在应用内部。
- Proxy 方式对业务更透明,但需要治理代理层的性能和高可用。
- 应用内嵌方式更灵活,但会增加业务代码和框架治理复杂度。
- 读写分离的核心副作用是主从延迟。
- 主从延迟会导致写后立即读时读取不到最新数据。
- 同步复制增强一致性,但牺牲写性能和吞吐量。
- 强制读主适合不能容忍延迟的关键业务场景。
- 会话分离适合保证用户“自己写后自己可见”。
- 读写分离的本质权衡是:读性能扩展与数据一致性之间的平衡。