ThinkPHP6多数据库切换需按需创建独立连接实例,关键在配置定义与运行时使用分离:一、在config/database.php中预定义connections;二、用Db::connect()获取实例并显式调用;三、模型通过protected $connection绑定固定库;四、动态库名需中间件注册并挂载;五、注意事务仅限单连接、避免循环新建连接、迁移命令不识别模型连接。

ThinkPHP6 的多数据库切换不是“改默认连接”,而是按需创建独立连接实例。关键在于分清配置定义和运行时使用两个阶段,用错方式会导致查的还是默认库。
一、提前在 config/database.php 中定义多个连接
必须先在配置文件里声明好所有要切换的数据库,不能只传 DSN 或临时数组(除非你明确要用 Db::connect($array)):
- 打开 config/database.php,确保
connections数组中已定义键名,例如:
'connections' => [
'mysql' => [ /* 默认库 */ ],
'log_db' => [
'type' => 'mysql',
'hostname' => '192.168.1.20',
'database' => 'app_logs',
'username' => 'log_user',
'password' => 'xxx',
'charset' => 'utf8mb4',
'prefix' => '',
],
'tenant_db' => [
'type' => 'mysql',
'hostname' => '10.0.0.5',
'database' => 'tenant_abc',
'username' => 'abc_user',
'password' => 'yyy',
'charset' => 'utf8mb4',
],
]
- 每个键名(如
'log_db')就是后续Db::connect()要传的字符串参数,大小写和下划线必须完全一致; -
database字段不能漏,否则报 No database selected,不是连不上,是连上了但没选库。
二、控制器中临时切换:用 Db::connect() 获取新实例
适用于单次查询、后台导出、跨库同步等场景。记住:它不改变默认连接,也不影响后续 Db::table() 的行为。
- 正确写法(必须赋值并显式使用):
$logDb = Db::connect('log_db');
$result = $logDb->table('operation_log')->where('status', 1)->select();
<p>// ✅ 正确:全部通过 $logDb 执行
$userDb = Db::connect('tenant_db');
$userDb->table('user')->insert(['name' => 'Alice']);</p>
- 错误写法(看似调了 connect,实际无效):
Db::connect('log_db'); // 对象被丢弃,无任何效果
Db::table('log')->select(); // 仍走默认库 ❌
- 支持直接链式调用,适合简单操作:
Db::connect('log_db')->table('error_log')->where('level', 'ERROR')->select();
三、模型类固定绑定某库:设 protected $connection
适用于读写分离、日志表、租户主库等长期稳定的连接关系。
- 在模型类顶部指定配置键名(不是 DSN,也不是数据库名):
namespace app\model;
use think\Model;
<p>class LogModel extends Model
{
protected $connection = 'log_db'; // 对应 config/database.php 中的键名
}</p>
- 这样所有操作都自动走该库:
LogModel::find(1)、(new LogModel())->save([...]); - 关联模型不会自动继承,若关联表也在同一库,需在关联定义里再加
'connection' => 'log_db'; - 这个属性只在模型首次加载时读取一次,运行时修改
$this->connection = 'xxx'不生效。
四、多租户/动态库名:中间件中注册 + 请求对象挂载
当数据库名、host 或用户随请求变化(如子域名 tenant1.example.com → tenant1 库),需在请求生命周期早期完成连接初始化。
- 在中间件中解析租户标识,组装配置并注册:
// middleware/TenantDbMiddleware.php
public function handle($request, \Closure $next)
{
$subdomain = $request->subDomain();
$dbName = 'tenant_' . $subdomain;
<pre class="brush:php;toolbar:false;">$config = [
'type' => 'mysql',
'hostname' => '10.0.0.10',
'database' => $dbName,
'username' => 'tenant_user',
'password' => '***',
'charset' => 'utf8mb4',
];
// 注册为可识别的连接名
Db::setConnectConfig($dbName, $config);
// 挂载到请求对象,供后续业务代码使用
$request->db = Db::connect($dbName);
return $next($request);}
- 控制器中统一使用挂载的连接,避免混用 Db::table():
class UserController extends BaseController
{
public function index(Request $request)
{
$users = $request->db->table('user')->select();
return json($users);
}
}
- 模型若也要走此连接,需在
initialize()中手动注入(不能改 $connection 属性):
protected function initialize()
{
$this->connection = app('request')->db;
}
五、额外注意:事务与性能边界
这些细节容易踩坑,务必确认清楚:
- 事务只在单个
Db::connect()返回的对象内有效,跨连接调用startTrans()和commit()是伪事务; - 不要在循环里反复调
Db::connect(),复用同一个变量; - 频繁新建连接会增加 MySQL 连接数,建议开启连接池或限制
pool_size; - 迁移命令
php think migrate:run不识别模型的$connection,只走database.default配置。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











