根本原因是ThinkPHP首次数据库操作即初始化并缓存连接实例,后续修改配置不重置已有连接;Db::name()等复用默认单例,与Db::connect()新建对象无关,且字段缓存、配置缓存均需手动清除。

ThinkPHP 动态修改数据库配置(比如用 Db::connect() 或改 config('database', $new))不立即生效,根本原因在于:框架在首次数据库操作时就已初始化并缓存了连接实例,后续修改只更新配置数组,不重置已有连接。
连接对象一旦创建就不再刷新
ThinkPHP 的数据库操作(如 Db::table('user') 或 Db::name('user'))会在第一次调用时自动触发默认连接初始化,并将该连接对象存入内部静态容器。之后所有未指定连接的 Db 操作,都复用这个已建立的连接——哪怕你后来调用了 Db::connect($newConfig),也只是把新配置写进配置池,不会中断或替换正在运行的连接。
-
典型现象:中间件里调了
Db::connect($tenantConfig),但控制器中Db::name('user')->select()仍连的是旧库 -
本质原因:
Db::name()走的是默认连接单例,和你新建的连接对象完全无关 -
验证方法:在控制器里打印
get_class(Db::getConnection()),会发现始终是同一个实例
配置缓存机制屏蔽了实时读取
框架启动时会把 config/database.php 的返回值完整加载进内存配置容器(think\Config),后续 config('database') 全部读内存,不再解析 PHP 文件。即使你手动改了文件内容,只要没清缓存或重启进程,Db::connect() 内部读到的仍是旧数组。
- 执行
php think clear:config可清空编译后的配置缓存(runtime/cache/config.php) - 若启用了 OPcache,还需重启 PHP-FPM,否则 PHP 字节码仍缓存着旧配置文件的解析结果
- 直接改
.env中的数据库参数(如DATABASE_HOST)也需同步清缓存,且确保config/database.php中使用env('DATABASE_HOST')读取
字段结构缓存干扰表操作感知
即使连接成功切换到新库,如果新库的表结构(如新增字段)和旧库不一致,而字段缓存未更新,查询可能报错或漏字段。ThinkPHP 默认缓存表结构到 runtime/schema/ 下,不会随连接切换自动刷新。
立即学习“PHP免费学习笔记(深入)”;
- 开发阶段建议在
config/database.php中设'fields_cache' => false - 上线后若修改了表结构,务必运行
php think clear或手动删掉runtime/schema/全部文件 - 也可在代码中调用
\think\facade\Db::clearCache()主动清字段缓存
正确做法:绕过默认连接池
要真正实现“按请求动态换库”,不能依赖全局 Db 单例,而应显式创建、传递和使用独立连接对象:
- 在中间件中组装配置数组,调用
$db = Db::connect($config)获取新实例 - 将该实例绑定到请求对象:
$request->db = $db - 控制器中统一用
$request->db->name('user')->select(),而非Db::name() - 模型类需重写
initialize(),手动指定连接:$this->connection = app('request')->db



















