ThinkPHP 6.0 多数据库配置必须在 config/database.php 的 connections 数组中单独、完整、显式定义每个连接,缺一不可的字段包括 type、hostname、database、username、password;连接名仅支持小写字母和下划线,Db::connect() 必须链式调用,模型需通过 protected $connection 显式绑定,否则静默回退 default。

ThinkPHP 6.0 不支持“批量添加数据库连接参数”这种模糊操作——它要求每个数据库连接都必须在 config/database.php 的 connections 数组中**单独、完整、显式定义**。所谓“批量”,实际是手动配置多个独立连接项,而非一键生成或循环注册。
connections 数组里每个连接必须填全这些字段
以新增一个名为 log_db 的 MySQL 连接为例,缺一不可:
-
type:必须是小写驱动名,如
'mysql'(不能写'MySQL'或'Mysql') -
hostname:IP 或域名,建议写死为
'127.0.0.1'或从.env安全读取:env('database2.hostname', '127.0.0.1') -
database:真实存在的库名,大小写敏感,比如
'sys_log' -
username 和 password:不能为空字符串(除非 MySQL 明确允许空密码),特殊字符如
@、/需提前 urlencode -
hostport:显式写上,如
'3306',不依赖默认值 -
charset 和 prefix:建议显式设为
'utf8mb4'和''(空字符串),避免跨库时前缀污染表名
连接名命名必须严格合规
键名(即连接标识符)不是随便起的,直接用于 Db::connect('xxx'),框架校验极严:
- 只允许小写字母 + 下划线,例如
'log_db'✅、'report_slave'✅ - 禁止点号(
'log.db'❌)、大写字母('LogDb'❌)、数字开头('2nd_db'❌) - 大小写完全敏感:写成
'LOG_DB'就会静默 fallback 到default
如何安全复用配置逻辑(模拟“批量”)
虽然不能自动批量注册,但可通过 PHP 数组语法减少重复代码。例如在 config/database.php 中这样组织:
立即学习“PHP免费学习笔记(深入)”;
(注意:这不是运行时动态生成,而是配置文件内的静态复用)```php
use think\Env;
$common = [
'type' => 'mysql',
'charset' => 'utf8mb4',
'prefix' => '',
'params' => [],
];
return [
'default' => 'mysql',
'connections' => [
'mysql' => array_merge($common, [
'hostname' => Env::get('database.hostname', '127.0.0.1'),
'database' => Env::get('database.database', 'thinkphp'),
'username' => Env::get('database.username', 'root'),
'password' => Env::get('database.password', ''),
'hostport' => Env::get('database.hostport', '3306'),
]),
'log_db' => array_merge($common, [
'hostname' => Env::get('database2.hostname', '127.0.0.1'),
'database' => Env::get('database2.database', 'sys_log'),
'username' => Env::get('database2.username', 'log_user'),
'password' => Env::get('database2.password', 'log_pass'),
'hostport' => Env::get('database2.hostport', '3307'),
]),
],
];
```
这样既保持结构清晰,又避免字段遗漏,也方便后续扩展第三个、第四个连接。
验证是否生效的关键动作
光写完配置不等于连上了,必须做两件事:
- 在控制器中执行
Db::connect('log_db')->query('SELECT 1'),不报错才算真正通了 - 查日志或开启 SQL 监听(
'trigger_sql' => true),确认执行语句确实发到了目标库,而不是静默走回default



















