Yii2多数据库需为每个库注册独立组件(如db2),跨库关联须分步查询并显式指定getDb(),跨库事务不可用MySQL本地事务,须用应用层补偿机制,QueryBuilder跨库需确保使用正确连接实例或全限定表名。

Yii2 多数据库配置怎么写才不冲突
多个数据库在 Yii2 里不是靠“自动识别”区分的,而是靠 db 组件名 + 显式指定。默认的 db 组件只能对应一个连接,想用第二个库,必须手动注册新组件(比如 db2),并在模型或查询中显式调用它。
常见错误是直接在 config/db.php 里塞两个数组,却不改组件名——结果只有最后一个生效;或者在模型里硬写 Yii::$app->db2->createCommand(...),但忘了在 config/web.php 里声明该组件。
- 在
config/web.php的components下新增独立组件:'db2' => ['class' => 'yii\db\Connection', 'dsn' => 'mysql:host=...', ...] - 避免重用
db配置数组做浅拷贝,容易漏掉charset或tablePrefix等关键项 - 不同库的
tablePrefix必须各自配,不能共用一个全局前缀
ActiveRecord 关联跨库表为什么总报错 “Table not found”
Yii2 的 ActiveRecord 默认只认当前模型绑定的 db 组件所连的库。即使你在 getRelation() 里写了 joinWith,底层 SQL 仍会把所有表都扔进同一个库查——跨库 JOIN 在 MySQL 本身就不支持(除非用 FEDERATED 引擎,但生产环境几乎不用)。
所以“跨库关联”本质是伪命题:你没法让一个 User 模型直接 hasOne(Order::class) 并自动跨库查出数据。
- 正确做法是分两步:先查主库模型(如
User),再用返回的外键字段(如order_id)去副库查Order模型 - 给副库模型显式指定连接:
public static function getDb() { return \Yii::$app->db2; } - 别在
relations里写跨库关联定义,否则with()会静默失败或抛出SQLSTATE[42S02]
事务跨库为什么会失效甚至死锁
MySQL 的本地事务(BEGIN/COMMIT)只对单个连接有效。Yii2 的 Transaction 类也是基于单个 Connection 实例封装的。当你试图用 $transaction = Yii::$app->db->beginTransaction() 同时操作 db 和 db2,实际只锁住了第一个库,第二个库完全游离在事务之外。
现象包括:A 库写入成功、B 库写入失败,但 A 不回滚;或者两个库都写入了,但逻辑上不一致。
- 跨库写入必须用应用层事务(Application-level transaction):手动控制每一步,失败时反向补偿(比如删掉已写的 A,或标记 B 为待重试)
- 如果两个库在同一个 MySQL 实例下,可考虑用 DB Link 方式统一走一个连接(改 DSN 为
mysql:host=...;dbname=db1和db2.table_name全限定名),但这要求用户有跨库 SELECT 权限,且不能用于 DDL - 切勿在事务块里混用
Yii::$app->db和Yii::$app->db2的createCommand()
Query Builder 跨库查询怎么写才不会拼错库名
QueryBuilder 本身不感知“库”,它只生成 SQL 字符串。真正决定查哪个库的是你调用的 Connection 实例。所以关键不是“怎么写 SQL”,而是“用哪个 db 实例执行”。一旦用了 db2,所有表名默认都在 db2 对应的库下找。
容易踩的坑是:在 db2 连接上执行 SELECT * FROM user,结果查到了主库的 user 表——这说明你根本没用对连接实例,或者 db2 的 DSN 还是指向了主库。
- 查副库必须明确用:
Yii::$app->db2->createCommand('SELECT * FROM order')->queryAll() - 需要跨库引用时,用全限定名:
Yii::$app->db->createCommand('SELECT u.name, o.status FROM user u JOIN db2.order o ON u.id = o.user_id')—— 注意这里db2.order是字符串字面量,不是变量 - 全限定名方式仅适用于同实例多库,且当前用户有目标库权限;跨实例必须分两次查
跨库最麻烦的从来不是语法,而是事务边界和关联语义的断裂。哪怕配置全对,只要在一个业务流里同时读写两个库,就得自己扛起一致性责任——框架不会帮你兜底。









