ThinkPHP 6 支持在配置文件中定义多个数据库连接,并通过 Db::connect 方法手动切换。读写分离场景下既支持自动部署参数配置,也支持手动指定主从连接,但建议明确指定连接以便更好地控制事务和调试。
先说结论:多数据库配置通过修改 config/database.php 实现,代码中手动切换连接比自动读写分离更可控,但框架也支持自动部署参数。
- 适合:多业务库隔离、主从读写分离、分库分表场景
- 先准备:备份原配置文件、确认新数据库账号权限、测试网络连通性
- 验收:验证连接切换是否生效、检查事务是否跨库异常、确认日志无连接报错
配置速用版
以下是多连接及自动读写分离的核心配置结构示例:
// config/database.php 部分配置
return [
'default' => 'mysql',
'connections' => [
// 多连接配置示例
'mysql' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'db_master',
'username' => 'root',
'password' => '',
],
'mysql_slave' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'db_slave',
'username' => 'root',
'password' => '',
],
// 自动读写分离配置示例(单连接名内部部署)
'mysql_deploy' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'db_name',
'username' => 'root',
'password' => '',
'deploy' => 1, // 开启读写分离
'read' => ['127.0.0.2', '127.0.0.3'], // 从库列表
'write' => ['127.0.0.1'], // 主库列表
],
],
];原理简述
ThinkPHP 6 的数据库连接采用懒加载模式,默认只初始化配置中 default 指定的连接。当业务需要访问多个数据库时,框架允许在 connections 数组中定义多个配置项。每个配置项代表一个独立的连接句柄,切换连接实际上是切换了 PDO 实例的初始化参数。
关于读写分离,虽然框架支持通过 deploy 参数自动识别读写,但在复杂业务中,手动指定连接名(如 Db::connect('mysql_slave'))能让开发者更清楚地知道当前查询走的是哪个库,避免在事务场景下因自动切换导致的数据不一致。
分步处理
第一步:修改数据库配置文件
打开项目根目录下的 config/database.php,找到 connections 数组。保持默认的 mysql 配置不变,复制一份修改为从库或第二业务库的配置,键名改为自定义标识,例如 mysql_slave 或 db_order。若需自动读写分离,可配置 deploy 参数。
第二步:代码中切换连接
在模型或控制器中,默认使用 Db::table() 或模型继承会走默认连接。如需切换,使用 Db::connect('配置键名')。例如:
use think\facade\Db;
use think\Model;
// 查询默认库
Db::table('user')->select();
// 查询从库
Db::connect('mysql_slave')->table('user')->select();
// 模型指定连接
class Order extends Model
{
protected $connection = 'db_order';
}第三步:处理事务边界(重要)
事务不能跨数据库连接。如果业务逻辑涉及两个库的写入,不要尝试在一个事务闭包中包裹两个 connect 的操作。Db::transaction() 默认绑定当前默认连接,跨库事务无法保证原子性。应分别在各自连接中处理事务,或通过应用层逻辑保证最终一致性。
// 错误示范:试图跨连接事务
Db::transaction(function() {
Db::connect('mysql')->table('a')->insert(...);
Db::connect('mysql_slave')->table('b')->insert(...); // 此操作不在同一事务中
});怎么验证是否生效
方法一:打印当前连接配置
在切换连接后,使用 Db::getConfig() 查看当前激活的连接信息,确认 hostname 或 database 字段是否符合预期。
Db::connect('mysql_slave');
print_r(Db::getConfig());方法二:查询特定数据
在主库和从库中插入不同的测试数据,切换连接后查询该表,根据返回结果判断当前连接的是哪个数据库。
方法三:查看数据库日志
开启数据库服务器的通用查询日志(General Log),观察应用发起请求时,数据库服务端接收到的连接来源和查询语句,确认是否命中了预期的数据库实例。
常见坑
1. 配置缓存导致不生效
ThinkPHP 开启配置缓存后,修改 database.php 可能不会立即生效。调试期间建议关闭配置缓存,或清除缓存文件。
清除缓存命令:
# 使用框架命令
php think clear
# 或手动删除 runtime 目录
rm -rf runtime/cache2. 模型连接属性优先级
模型类中定义的 $connection 属性优先级高于全局默认配置,但低于 Db::connect() 的动态调用。如果模型硬编码了连接名,动态切换可能会被覆盖,需注意检查模型定义。
3. 连接资源未释放
频繁动态切换连接可能会创建多个 PDO 实例。虽然框架有连接池管理,但在长脚本或队列任务中,建议复用连接对象,避免短时间内创建大量数据库连接导致服务端报错。
4. 读写分离的数据延迟
如果配置了主从分离,刚写入主库的数据立即查询从库可能查不到(主从同步延迟)。业务上涉及“写后立刻读”的场景,强制走主库连接,不要依赖自动路由。
5. 跨库事务风险
再次强调,ThinkPHP 的事务机制基于单一 PDO 连接。跨库操作无法使用 Db::transaction 保证原子性,需通过分布式事务方案或业务补偿机制处理。