forcemaster仅对当前ar查询或command生效,需为每个操作单独调用;事务内默认走主库,但事务外关键读必须显式forcemaster,且需确保连接实例一致,否则无效。

forceMaster 在 Yii2 里怎么生效
forceMaster 不是全局开关,它只在当前 ActiveRecord 查询或 Command 执行时起作用。默认情况下,Yii2 的读写分离靠 Connection::getSlave() 自动选从库,但一旦你调用了 forceMaster(),后续的 createCommand()、find() 等操作才会强制走主库——前提是这些操作发生在同一个 DB 连接实例生命周期内。
常见错误现象:在控制器里调用 $model->find()->forceMaster()->one(),接着又写 Yii::$app->db->createCommand(...)->execute(),后者依然可能走从库,因为 Yii::$app->db 是独立连接对象,没继承前者的 forceMaster 状态。
- 必须对每个要强制主库的操作单独调用
forceMaster(),不能“设一次管全程” - 事务内所有查询默认已走主库,无需额外
forceMaster();但事务外的关联查询(比如事务提交后立刻查刚插入的记录)仍需显式加 - 如果用的是
ActiveRecord::getDb()获取连接,记得确认它和Yii::$app->db是同一个实例,否则配置不同步
事务里写完马上读,为什么还是读到旧数据
这不是 Yii2 的 bug,而是 MySQL 主从复制本身的延迟 + Yii2 连接复用机制共同导致的。即使你在事务中写了数据,事务一提交,下一条 find()->one() 如果没指定 forceMaster(),Yii2 可能从连接池里捞出一个刚用过的从库连接来执行——而此时从库还没同步完。
使用场景典型如:用户注册后立即跳转到个人页,页面里查 User::findOne($id),结果返回 null。
- 最稳妥做法:事务内写完,后续关键读操作直接复用同一连接并加
forceMaster(),例如$user = User::find()->db(Yii::$app->db->master)->one() - 避免依赖“刚写完就自动可见”,尤其在高并发或跨服务调用时,主从延迟可能达数百毫秒
- 不要试图用
sleep(0.1)补救——不可靠,且拖慢响应
配置 master 和 slaves 时容易漏掉的关键点
很多人按文档配了 master 和 slaves 数组,却忘了 Yii2 默认不开启读写分离:必须把 enableSlaves 设为 true,否则 slaves 配置完全被忽略。
另一个隐蔽坑是连接参数不一致。比如主库开了 charset=utf8mb4,但从库连接里没写,可能导致中文乱码或隐式转换失败,看起来像“读不到数据”。
-
enableSlaves必须显式设为true,它默认是false - 主从连接的
charset、attributes(如PDO::ATTR_EMULATE_PREPARES)、tablePrefix应保持一致 - 从库列表为空数组
[]时,Yii2 不会报错,但所有读请求 fallback 到主库——这容易掩盖配置问题,建议加日志确认Connection::getSlave()是否真返回了从库连接
什么时候不该用 forceMaster,而该换方案
高频、小数据量的“写后即读”,比如订单创建后查订单号,用 forceMaster 没问题。但如果是批量导入后立刻查统计结果,或者需要强一致的权限校验,硬扛 forceMaster 会让主库压力陡增,还可能因主库过载进一步拉大主从延迟。
这时候更合理的做法是:把强一致性要求的逻辑收拢到主库完成,或引入缓存层兜底(如写完更新 Redis),而不是让所有读都切回主库。
- 避免在循环里反复调用
forceMaster(),比如 for 循环插入 100 条再逐条查,应改用单次主库查询或事务内完成 - 报表类、后台管理类场景,若允许秒级延迟,干脆关掉
forceMaster,接受最终一致性 - 跨微服务场景下,
forceMaster失效(对方服务不认你的连接配置),得靠消息队列+状态轮询或 CDC 同步
主从延迟不是配置调得细就能根治的问题,它本质是架构取舍。真正难的不是让某条 SQL 走主库,而是判断哪条该走、哪条可以忍、哪条其实不该存在。











