从库查不到刚写入的数据是因主从复制延迟导致的最终一致性表现。INSERT/UPDATE后立即从从库SELECT可能为空,需通过SQL Hint、注解或事务内读写同库等方式强制走主库。

为什么从库查不到刚写入的数据
MySQL 读写分离后,写操作落在主库,读操作默认路由到从库;但主从复制有延迟(即使毫秒级),INSERT 或 UPDATE 提交后立刻在从库 SELECT,很可能查不到或查到旧值。这不是 bug,是最终一致性模型的必然表现。
常见错误现象:INSERT INTO orders ...; SELECT * FROM orders WHERE id = LAST_INSERT_ID(); 在从库执行时返回空——因为 binlog 还没同步过去。
- 复制延迟受网络、从库负载、大事务影响,无法完全消除
- 半同步复制能降低延迟,但不保证实时
- 应用层不做控制时,框架(如 MyBatis Plus 多数据源)或中间件(ShardingSphere、Atlas)通常默认走从库
强制走主库的三种实操方式
核心思路:绕过读写分离中间件/代理的自动路由逻辑,显式指定查询发往主库。具体方法取决于你用的架构层级。
- 如果用的是
ShardingSphere-JDBC:在 SQL 注释中加/* sharding hint: write */,例如/* sharding hint: write */ SELECT * FROM user WHERE id = 123; - 如果用的是
MyBatis+ 自定义多数据源(如AbstractRoutingDataSource):在 Service 方法上加自定义注解(如@Master),并在determineCurrentLookupKey()中识别该标记,返回"master"数据源名 - 如果直连 MySQL 且用
mysql-proxy或MaxScale:部分版本支持 SQL Hint,如SELECT /*+ MAXSCALE route-to=master */ * FROM t;,需确认代理是否开启 hint 解析
注意:/* ... */ 注释必须紧贴 SELECT 开头,不能换行或有空格隔开,否则中间件可能无法识别。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
用事务保证读写同库的关键细节
在同一个事务中执行写和读,多数读写分离方案会将整个事务路由到主库(前提是事务未被拆分)。但这不是绝对的,依赖中间件行为。
-
Spring @Transactional默认传播行为是REQUIRED,只要不手动切数据源,事务内所有 JDBC 操作都会复用同一个连接——而连接来自主库数据源 - 风险点:如果事务中调用了另一个被
@Slave注解修饰的方法,或触发了动态数据源切换,连接可能被换掉,导致后续读走从库 - 不要依赖“事务自动走主库”而不验证——建议在事务方法开头加日志,打印当前数据源名,或用
Connection.isReadOnly()辅助判断 - 长事务会占用主库连接,增加主库压力;非必要不把耗时查询塞进写事务里
什么场景下必须强制主库,什么可以接受延迟
不是所有“刚写完就查”都需要强一致。先判断业务语义:
- 必须强一致:支付结果页查订单状态、后台审核后立即刷新详情、用户注册后跳转个人中心显示昵称
- 可接受秒级延迟:商品销量计数、评论列表、运营后台的统计报表
- 折中方案:对关键读请求加
SELECT ... FOR UPDATE(需在主库执行),既防止并发修改,又自然走主库;但会阻塞,慎用 - 更轻量替代:写完后
SLEEP(0.1)再查?不推荐——不可靠、难维护、掩盖问题本质
最易被忽略的一点:前端重定向(redirect after post)本身已天然规避了大部分刚写即读问题——因为浏览器发起新请求,此时主从延迟大概率已收敛;真正危险的是 AJAX 连续调用,且读接口没做主库路由。


















