Yii 2.0.51 默认关闭读写分离,必须显式配置 readWriteSplitting => true 并完整定义 slaves 数组;只读查询如 find()->one() 或 createCommand()->queryAll() 走从库,而 INSERT/UPDATE/DELETE、事务内所有操作及裸 createCommand() 均走主库;从库宕机不自动降级,需手动 try/catch fallback;无延迟检测与重试机制。

Yii 框架的读写分离不是“开了就自动高可用”,它默认不启用,必须显式配置 readWriteSplitting=true,且从库宕机时不会自动 fallback,不处理主从延迟,也不做健康检查——这些都得你自己补。
readWriteSplitting 必须手动打开,否则所有查询走主库
Yii 2.0.51 及之后版本默认关闭读写分离。即使你配了 slaves 数组,SELECT 依然走主库,因为框架根本没启动路由逻辑。
- 必须在
db组件配置中加'readWriteSplitting' => true -
slaves是数组,哪怕只有一台从库也得写成[['dsn' => '...']],不能是单个配置对象 -
masterConfig和slaveConfig是模板,用于合并到每个主/从实例配置中,不能省略或复用同一引用(否则密码、超时等会串) - 错误写法:
'slaves' => $commonConfig(PHP 引用传递导致覆盖);正确写法:'slaves' => [['dsn' => '...', 'username' => '...']]
哪些查询真走从库?哪些看似只读却仍连主库
Yii 不靠 SQL 关键字判断读写,而是靠调用链上下文。裸 SQL 或事务一介入,就容易“掉回主库”。
- 走从库:ActiveRecord 的
find()->one()、find()->all()、createCommand()->queryAll()(注意是Yii::$app->db->createCommand(),不是 new) - 不走从库:
createCommand($sql)->queryOne()(没走组件代理)、findOne()(内部可能触发锁或事务上下文)、带joinWith()的关联查询(部分场景被标记为非安全读) - 事务内所有操作强制走主库:
$tx = Yii::$app->db->beginTransaction(); $user = User::find()->one();→ 这个one()仍走主库,无法绕过 - 想查旧数据做比对?只能提前在事务外用
Yii::$app->db->slave->createCommand()->queryOne()拿好,缓存到变量里
从库挂了怎么办?Yii 不会自动降级
Yii 的 Connection 在选从库失败时,会抛出 yii\db\Exception,而不是静默切主库。线上不加兜底,直接 500。
- 必须手动 try/catch:
try { $data = Yii::$app->db->createCommand(...)->queryAll(); } catch (\yii\db\Exception $e) { $data = Yii::$app->db->master->createCommand(...)->queryAll(); } - 可配合
serverStatusCache避免反复试探死节点:'serverStatusCache' => ['class' => 'yii\caching\FileCache'],并设'serverRetryInterval' => 60(秒) - 注意:
serverStatusCache只记“死”,不记“慢”。从库响应超时(如 10 秒)仍会被重试,除非 PDO 层抛异常 - 别依赖“全从库不可用时自动连主库”——这行为只在首次连接从库失败时触发,后续请求若持续失败,仍会不断重试从库直到超时
主从延迟下怎么读最新数据?没有银弹,只有取舍
Yii 不提供延迟检测、重试或一致性读机制。所谓“读写分离”,只是路由,不是一致性保障。
- 关键路径(如支付结果页、订单详情)必须显式走主库:
Yii::$app->db->master->createCommand($sql)->queryOne() - ActiveRecord 中可用
->master()方法:User::find()->master()->where(['id' => 123])->one() - 不要在刚
save()后立刻用find()->one()查——大概率查不到,除非你确认从库延迟 - 若需强一致,考虑应用层加缓存(如 Redis)暂存刚写入的数据,或改用分布式事务(代价更高)
最常被忽略的一点:readWriteSplitting 开关和 slaves 配置必须同时存在才生效;而生产环境的从库连接失败,不会触发任何告警或日志记录——除非你主动监听 yii\db\Connection::EVENT_AFTER_OPEN 或捕获异常并上报。


















