Yii2通过db组件的slaves数组配置主从库,读操作自动路由至从库,写操作走主库;需显式设置master=>true/false,各库dsn/username/password独立填写,强一致场景用useMaster()强制走主库,事务中须手动兜底确保读主库。

Yii2里怎么配主从库,让读走从库、写走主库
默认情况下,Yii2的yii\db\Connection只连一个数据库;要实现读写分离,得用yii\db\Connection的slaveConfig和slaves机制,而不是自己手动切连接。
关键不是“加两个DB配置”,而是让主连接知道哪些是它的从库,并在执行SELECT时自动挑一个从库去连——这一步由Yii2内部的getSlave()方法控制。
- 主库配置写在
db组件顶层,必须设'master' => true(虽然不写也默认是master,但显式声明更稳) - 从库配置放在
'slaves'数组里,每个从库也要带'master' => false(否则某些场景下可能被误判为主库) - 所有主从库的
dsn、username、password必须各自独立填写,不能靠slaveConfig继承(它只继承部分字段,比如charset,但不继承认证信息)
示例片段:
'db' => [
'class' => 'yii\db\Connection',
'master' => true,
'dsn' => 'mysql:host=master-host;dbname=myapp',
'username' => 'root',
'password' => 'xxx',
'charset' => 'utf8mb4',
'slaves' => [
[
'master' => false,
'dsn' => 'mysql:host=slave1-host;dbname=myapp',
'username' => 'ro_user',
'password' => 'yyy',
'charset' => 'utf8mb4',
],
[
'master' => false,
'dsn' => 'mysql:host=slave2-host;dbname=myapp',
'username' => 'ro_user',
'password' => 'yyy',
'charset' => 'utf8mb4',
],
],
],
为什么刚写完数据,立刻查不到(从库延迟导致的查询不一致)
主从复制有延迟,Yii2默认对所有SELECT都走从库,包括事务刚提交后的find()。这不是Yii2的bug,是MySQL主从架构的天然限制。
解决思路不是关掉从库,而是“在需要强一致的场景下,主动切回主库查”。Yii2提供了useMaster()方法,但它只对当前查询生效,且必须在Query对象上链式调用。
-
User::find()->where(['id' => 123])->one()→ 走从库 -
User::find()->useMaster()->where(['id' => 123])->one()→ 强制走主库 -
Yii::$app->db->useMaster()->createCommand('SELECT ...')->queryOne()→ 也生效 - 注意:
useMaster()对ActiveRecord::findOne()等静态快捷方法无效,它们不暴露Query对象,得改用find()链式写法
事务里读操作为什么还是去了从库?
Yii2的读写分离逻辑在QueryBuilder或Command生成阶段就决定了走哪条连接,而事务本身不改变这个路由决策。也就是说:即使你在事务中执行SELECT,只要没显式调用useMaster(),它仍可能发到从库——而这会破坏事务隔离性(尤其在REPEATABLE READ下可能读到旧快照)。
正确做法是:只要进了事务,所有读都该走主库。Yii2不自动识别事务上下文,得人工兜底。
- 在
beforeAction或Service层开头判断Yii::$app->db->getIsActive()是否为true(即事务已开启) - 或者更简单:统一用
Yii::$app->db->beginTransaction()后,后续所有find()都显式加useMaster() - 避免在事务中混用
AR::find()和原生createCommand()却不加useMaster(),容易漏掉某一句
配置了从库但实际没流量过去,怎么排查
最常见原因是slaves数组为空或格式错误,导致getSlave()始终返回null,最终退化为全走主库。
验证方式很简单:临时在yii\db\Connection::getSlave()里加个var_dump($this->slaves),看是否真能取到从库配置;或者直接在应用里执行Yii::$app->db->getSlave() !== null,返回false就说明配置没生效。
- 检查
slaves是不是写在db组件顶层,而不是嵌套在slaveConfig里(slaveConfig只是模板,真正起作用的是slaves数组) - 确认PHP没报Warning:比如从库
dsn少写了host=,会导致PDO构造失败,Yii2会静默跳过该从库 - 观察日志:开启
'enableLogging' => true,查SELECT语句的日志里是否出现从库的host名;没出现就是根本没路由过去
主从分离不是配完就一劳永逸的事。延迟、事务、连接健康度、DNS解析失败……每处都可能让请求悄悄绕回主库,或者卡死在某个坏掉的从库上。上线前一定得用真实读写混合流量压测,光看配置对不对没用。










