yii多数据库切换依赖组件名路由和读写语义自动分流:每个库须注册为独立组件(如dbmain、dbstats),ar模型通过重写getdb()指定连接,主从分离由slaves/slaveconfig配置实现,写操作走主库、读操作随机选从库,跨库事务不支持,需业务层补偿。

Yii 框架里多数据库切换不是靠手动选库,而是靠组件名路由和读写语义自动分流——只要配置对、调用对,框架自己决定走哪个连接。
db 组件名就是数据库连接的“开关”
每个数据库连接必须注册为一个独立的组件,名字不能叫 db(除非你只用一个),比如 dbMain、dbStats、dbLog。组件名直接决定你在代码里怎么用它:
-
Yii::$app->dbMain->createCommand(...)->execute()→ 走主业务库 -
Yii::$app->dbStats->createCommand(...)->queryAll()→ 走统计库 - AR 模型默认用
db,若要换库,得在模型类里显式指定:public static function getDb() { return Yii::$app->dbStats; } - 别把多个库都配成
db,否则后注册的会覆盖前一个,只生效最后一个
主从分离靠 slaves 和 slaveConfig 配置,不是靠写死 IP
主从不是“多个 db 组件”,而是一个 db 组件内部的结构化配置。框架会自动判断:写操作(execute、insert、update、delete)走主库;读操作(queryAll、findOne、AR 查询)随机挑一个从库。
-
slaves是数组,每个元素是完整 DSN 配置,支持不同主机、端口、甚至不同账号 -
slaveConfig是公共配置抽离,比如统一设PDO::ATTR_TIMEOUT => 5,避免重复写 - 从库宕机时,框架默认跳过该节点,不抛异常,但不会自动重试或降级到主库读 —— 这点容易被忽略,线上得配合监控告警
- 如果从库延迟大,AR 的
save()后立刻findOne()可能查不到刚写入的数据,得加Yii::$app->db->master->createCommand(...)->queryOne()强制走主库
跨库事务只能在单个 Connection 内生效
Yii 的 beginTransaction() 只作用于当前连接实例。你不能用 dbMain 开启事务,再往 dbStats 里写数据并一起提交。
- 真要跨库原子性,得上分布式事务(如 XA),但 MySQL 默认不启用,且 Yii 不原生支持
- 更现实的做法是:业务层补偿(如记录本地事务日志 + 定时校对)、或改用最终一致性(消息队列异步同步)
- 常见误操作:
Yii::$app->dbMain->beginTransaction()后调Yii::$app->dbStats->createCommand(...)->execute()—— 这两个操作完全无关,回滚只影响dbMain
环境隔离下,db.php 别硬编码密码
生产环境的数据库凭证绝不能写死在 config/db.php 里,尤其当该文件被 Git 跟踪时。
- 正确做法:在
config/main-local.php或config/prod/db.php中加载,且该文件加入.gitignore - 敏感字段如
username、password应优先从环境变量读取:'password' => getenv('DB_PASSWORD') ?: '' - 本地开发用
YII_ENV_DEV加载dev/db.php,生产用YII_ENV_PROD加载prod/db.php,避免配置混用 - 测试环境若复用生产库配置,记得关掉
enableSchemaCache,否则表结构变更后缓存不更新,AR 查询可能报错“Unknown column”
主从和多库最易出问题的地方,不在配置语法,而在对“读写语义”的误判和跨连接事务的想当然。上线前务必用真实流量路径验证主从分流是否生效、从库延迟是否可接受、错误场景下是否静默失败而非崩掉整个请求。











