db::connect()返回新query实例,不改变全局db默认连接;模型$connection静态绑定且仅影响当前模型,关联需显式指定;多租户应使用db::setconnectconfig()按需注册连接。

Db::connect() 返回的是新实例,不是“切换”默认连接
调用 Db::connect('mysql_log') 确实能连上日志库,但它返回一个全新的 Query 实例,和全局的 Db 静态类完全无关。后续所有 Db::table()、Db::name() 仍走 default 连接——这不是 bug,是设计逻辑。
常见错误现象:Db::connect('log')->table('log')->insert() 成功了,但下一行 UserModel::find(1) 还查主库。因为模型不认你刚建的那个 Query 实例。
- 必须显式保存返回值:
$logDb = Db::connect('log');,之后所有操作都基于$logDb->table(...) - 不能在 for 循环里反复调用
Db::connect(),否则快速耗尽 MySQL 连接数(报SQLSTATE[HY000] [1040]) - 若需跨模块复用,建议绑定到容器:
app()->bind('log_db', function () { return Db::connect('log'); });
模型类的 $connection 是静态绑定,运行时改配置无效
protected $connection = 'mysql_log'; 在模型类加载时就固化了,它只读一次,不会响应运行时对配置的修改(比如中间件里改了 Config::set())。更关键的是:这个设置只影响当前模型,不影响其关联模型。
例如 UserModel 设了 $connection = 'tenant_a',但它的 hasOne('Profile') 关联指向 ProfileModel,而后者没设 $connection,那关联查询仍走默认库。
- 关联定义中必须显式指定:
hasOne('Profile')->connection('tenant_a') - 若租户 ID 来自请求,可在模型
initialize()中动态赋值:$this->connection = app('request')->tenant_id ? 'tenant_' . app('request')->tenant_id : 'mysql'; - 不要在控制器里临时改模型属性:
UserModel::$connection = 'xxx'—— 它是静态属性,会污染后续请求
多租户场景下,Db::setConnectConfig() 是唯一可靠方案
ThinkPHP 6.1+ 的 Db::setConnectConfig() 允许运行时注册带标识的新连接配置,再通过 connect('tenant_123') 显式使用。这比硬编码一堆预置连接名更灵活,也避免了 database.php 配置爆炸。
典型做法是在中间件中解析租户标识(如子域名),生成配置并注册:
// 中间件内
$tenantId = $request->domain(); // 或从 header / route 提取
$config = [
'type' => 'mysql',
'hostname' => '192.168.1.' . substr($tenantId, -3),
'database' => 'db_' . $tenantId,
'username' => 'user_' . $tenantId,
'password' => '***',
];
Db::setConnectConfig('tenant_' . $tenantId, $config);
- 注册后即可在模型或服务中调用:
UserModel::connect('tenant_abc')->find(1) - 别为每个租户预定义完整配置数组(上千租户时内存爆炸),按需生成 + 缓存更稳
-
Db::setConnectConfig()不会自动注入到模型默认连接链路,仍需显式connect()
事务无法跨库,模型关联失效本质是连接隔离
ThinkPHP 的事务严格绑定在单个 Query 实例上。$db1->startTrans() 和 $db2->startTrans() 是两个孤立事务,commit 只对各自连接生效。模型关联失效,根源正是不同模型用了不同连接实例,彼此无上下文共享。
例如订单写主库、日志写日志库,想一起提交?框架原生做不到。
- 不要尝试在同一个事务块里混用多个
Db::connect()实例 - 强一致性场景必须退到业务层补偿:先写主库,成功后再发消息异步写日志库
- 本地测试时务必用
show processlist看连接状态,确认没有大量 Sleep 连接堆积
真正容易被忽略的点是:你以为模型设了 $connection 就万事大吉,其实关联、软删除、事件回调等所有底层操作,全依赖该模型初始化时绑定的那个连接实例——换库不重实例,就永远查不到目标数据。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











