主从延迟导致读到过期数据,你怎么解决?
先说结论
主从延迟的解决思路有三个层次:
① 业务层:关键读走主库 —— 最简单有效
② 架构层:减小延迟 —— 半同步复制、并行复制
③ 设计层:避免依赖强一致性 —— 缓存、消息队列
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
主从复制是MySQL高可用的基础,但主从延迟是生产环境的常客。写了主库但从库还没同步到,读从库就读到了旧数据。比如用户刚下单就查订单,结果显示“无订单”。今天把这个问题的原因和解决方案讲清楚。
为什么会有延迟?
MySQL主从复制的过程:主库写Binlog → 从库IO线程拉取Binlog → 从库SQL线程回放。延迟主要发生在第三步:从库回放速度跟不上主库写入速度。
常见原因:主库有大事务(比如一次性更新100万行)、从库硬件性能差、从库查询压力大。
解决方案一:关键读走主库(最简单有效)
对于“刚写完立刻读”的场景——比如下单后查订单、修改密码后登录——直接从主库读。
// 业务代码示例(用注解指定走主库)
@Master // 自定义注解,强制走主库
public Order getOrderById(Long orderId) {
return orderMapper.selectById(orderId);
}
这是我在每家公司都用的方案。简单、可靠、没有副作用。只有对一致性要求高的读才走主库,其他读仍然走从库。
解决方案二:半同步复制(减小延迟)
普通的异步复制:主库写完Binlog就返回成功,不等从库确认。半同步复制:主库等至少一个从库确认收到Binlog后才返回。
-- 开启半同步复制
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = ON;
SET GLOBAL rpl_semi_sync_master_timeout = 1000; -- 1秒超时降级为异步
半同步保证了Binlog已到达从库,但从库回放仍然可能有延迟。所以半同步只是减小延迟,不能完全消除。
解决方案三:缓存层避免依赖强一致性
对于“写完后读”的场景,可以把写入的数据同时写入Redis缓存,读的时候先读Redis。这样即使从库有延迟,用户也能立刻看到最新数据。
实战经验
我在元宇宙社交App的做法是“分场景处理”:
• 下单/支付后查订单 → 走主库(强一致性)
• 个人主页/动态列表 → 走从库(允许短暂延迟)
• 热门内容/插序 → 走Redis缓存(不依赖DB)
不是所有场景都需要强一致性。识别哪些场景需要、哪些不需要,是架构设计的核心。
总结
主从延迟的解决思路:关键读走主库 > 半同步复制 > 缓存层。面试时能说清楚“不同场景用不同方案”,而不是一个方案打天下,就很有水平。
下一篇聊一个每个后端开发都会遇到的场景:高并发下单时库存超卖了,怎么用MySQL保证不超卖?
你们生产环境的主从延迟一般多少?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长