ThinkPHP6 如何配置多数据库连接切换读写分离

文章导读
ThinkPHP 6 支持在配置文件中定义多个数据库连接,并通过 Db::connect 方法手动切换。读写分离场景下既支持自动部署参数配置,也支持手动指定主从连接,但建议明确指定连接以便更好地控制事务和调试。
📋 目录
  1. 配置速用版
  2. 原理简述
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
A A

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 参数。

第二步:代码中切换连接

ThinkPHP6 如何配置多数据库连接切换读写分离

在模型或控制器中,默认使用 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());

方法二:查询特定数据

在主库和从库中插入不同的测试数据,切换连接后查询该表,根据返回结果判断当前连接的是哪个数据库。

ThinkPHP6 如何配置多数据库连接切换读写分离

方法三:查看数据库日志

开启数据库服务器的通用查询日志(General Log),观察应用发起请求时,数据库服务端接收到的连接来源和查询语句,确认是否命中了预期的数据库实例。

常见坑

1. 配置缓存导致不生效

ThinkPHP 开启配置缓存后,修改 database.php 可能不会立即生效。调试期间建议关闭配置缓存,或清除缓存文件。

清除缓存命令:

# 使用框架命令
php think clear

# 或手动删除 runtime 目录
rm -rf runtime/cache

2. 模型连接属性优先级

ThinkPHP6 如何配置多数据库连接切换读写分离

模型类中定义的 $connection 属性优先级高于全局默认配置,但低于 Db::connect() 的动态调用。如果模型硬编码了连接名,动态切换可能会被覆盖,需注意检查模型定义。

3. 连接资源未释放

频繁动态切换连接可能会创建多个 PDO 实例。虽然框架有连接池管理,但在长脚本或队列任务中,建议复用连接对象,避免短时间内创建大量数据库连接导致服务端报错。

4. 读写分离的数据延迟

如果配置了主从分离,刚写入主库的数据立即查询从库可能查不到(主从同步延迟)。业务上涉及“写后立刻读”的场景,强制走主库连接,不要依赖自动路由。

5. 跨库事务风险

再次强调,ThinkPHP 的事务机制基于单一 PDO 连接。跨库操作无法使用 Db::transaction 保证原子性,需通过分布式事务方案或业务补偿机制处理。