← 返回题目列表

数据库读写分离如何设计?会带来哪些一致性问题?

高频 中等 第 11 / 33 题 更新于 2026/07/28
数据库设计读写分离主从复制一致性

简化版

读写分离通常是主库承担读写中的写请求,从库分担读请求,用来提升读吞吐和隔离查询压力。它的核心问题是主从延迟:写成功后读从库可能读到旧数据,所以写后读、事务内读、强一致读通常要走主库。

详细版

读写分离设计要点:

  • 写请求走主库;
  • 普通非关键读可以走从库;
  • 写后立即读、事务内读、强一致读走主库;
  • 从库可按业务隔离,比如报表从库、只读从库;
  • 监控复制延迟,延迟过高时读主或降级;
  • 客户端或中间件要识别事务上下文;
  • 故障切换后要更新主库路由。

读写分离解决的是读压力,不解决单条慢 SQL;慢查询打到从库也会拖慢复制,甚至导致延迟扩大。

完整版教学

一、读写分离的前提是复制

读写分离通常基于主从复制。主库接受写入,从库通过日志复制同步数据。应用层或数据库代理根据 SQL 类型和一致性要求把请求路由到主库或从库。

它适合读多写少的系统,比如内容展示、商品查询、资讯列表。但如果系统写多读少,收益就有限。

二、主从延迟是核心矛盾

复制不是瞬间完成的。主库提交成功后,从库可能还没重放到这条变更。

典型问题:

  1. 用户提交订单;
  2. 写主库成功;
  3. 订单列表读从库;
  4. 从库还没同步,用户看不到刚提交的订单。

这不是 bug,而是异步复制和读写分离的天然结果。

三、哪些读必须走主库

常见强制读主场景:

  • 写操作之后短时间内的读;
  • 同一个业务事务内的读;
  • 支付、库存、订单状态等强一致数据;
  • 管理后台刚修改后立即预览;
  • 从库延迟超过阈值时。

有些系统会在用户写入后给会话打标,接下来几秒内该用户读请求走主库,这就是会话一致性的一种实现。

四、从库不是慢查询垃圾桶

很多人以为复杂查询丢到从库就安全。实际上从库也要执行复制任务。如果报表查询长时间占用 CPU、I/O 或锁资源,复制延迟会变大,读到旧数据的概率也变高。

更合理的做法是把报表库、搜索系统、OLAP 系统和在线只读从库分开,避免互相影响。

五、路由层要理解事务

不能简单看到 SELECT 就发从库。事务里可能先写后读:

begin;
update account set balance = balance - 100 where id = 1;
select balance from account where id = 1;
commit;

这个 select 必须读主库,否则就不符合事务内一致性。

六、高可用切换也会影响读写分离

主库故障后,需要提升新主库,并让应用或代理更新路由。如果路由更新不及时,可能写到旧主或读到错误节点。

所以读写分离不是单纯加几个从库,还要配合故障检测、主库选举、连接切换和延迟监控。

设计路由时最好把“读一致性级别”变成明确规则,而不是让开发凭感觉选择数据源。例如普通列表默认读从库,支付结果、库存扣减、订单创建后 5 秒内读主库;复制延迟超过 1 秒时摘除对应从库。规则越明确,线上问题越容易定位。

七、常见误区与追问

读请求类型推荐路由原因
首页、列表、非关键展示从库可接受短暂旧数据
写后立即查询主库需要读到刚写入的数据
事务内查询主库保持事务上下文一致
从库延迟超过阈值主库或降级避免旧数据扩大业务影响

记忆钩子:读写分离的难点不是“SELECT 去从库”,而是识别哪些 SELECT 不能去从库。

用时间线看主从延迟:

T0 主库提交订单 order_id=1001
T1 应用返回创建成功
T2 用户刷新订单列表,读从库
T3 从库 800ms 后才重放到 order_id=1001

如果用户在 T2 查询,就会看到“订单不存在”。所以很多系统会在写成功后给当前用户会话设置 2 到 5 秒读主窗口,或者根据复制延迟动态决定是否读主。

  • 误区:读写分离能解决所有数据库性能问题。 它主要扩展读吞吐,慢 SQL、坏索引和大事务仍然要单独治理。
  • 误区:所有 SELECT 都可以走从库。 写后读、事务内读、强一致读和延迟异常时都应该读主。
  • 误区:从库可以随便跑报表。 重报表会抢 CPU/I/O,拖慢复制,导致更多业务读到旧数据。
  • 追问:如何缓解写后读不一致? 会话读主、关键接口强制读主、延迟监控动态路由、业务提示稍后刷新都可以组合使用。
  • 追问:主从延迟怎么监控? 关注复制位点差、延迟秒数、Relay Log 重放状态,以及业务层写入后可见性探测。
  • 追问:故障切换后有什么风险? 路由可能还指向旧主或落后从库,需要代理、连接池、配置中心和应用缓存一起切换。

八、加强记忆

读写分离提升读吞吐,但代价是主从延迟和路由复杂度。普通读可以走从库,写后读、事务内读、强一致读走主库;从库也要保护,慢查询会拖复制。它解决读压力,不解决烂 SQL。