thinkphp 6 主从配置需满足:default 必须存在且 type=‘mysql’,slave 为含完整数据库参数的数组;读写分离默认生效,select 轮询从库,INSERT/UPDATE/delete 走主库;强一致性读需显式调用 ->master(true)。

ThinkPHP 6 的 database.php 怎么配主从?
主从配置不是加个“slave”键就完事,TP6 要求显式声明 type 为 mysql,且主库必须叫 default,从库必须放在 slave 数组里——漏掉任一条件都会降级成单库连接。
常见错误现象:Db::table('user')->select() 始终走主库,slave 配置完全不生效。
-
default必须存在,且不能是空数组;它只负责写操作和未指定读写意图的查询 -
slave是数组,每个元素是一个完整数据库配置(含hostname、username等),不是只填 IP - 所有从库配置里不要设
deploy或rw_separate,TP6 已废弃这些旧键 - 如果用了 pdo 连接池或长连接,记得在从库配置中加
'params' => [PDO::ATTR_PERSISTENT => true],否则每次查从库都重建连接
示例关键片段:
'default' => [ 'type' => 'mysql', 'hostname' => 'master.example.com', 'database' => 'app_db', 'username' => 'root', 'password' => 'xxx', ], 'slave' => [ [ 'type' => 'mysql', 'hostname' => 'slave1.example.com', 'database' => 'app_db', 'username' => 'ro_user', 'password' => 'yyy', ], [ 'type' => 'mysql', 'hostname' => 'slave2.example.com', 'database' => 'app_db', 'username' => 'ro_user', 'password' => 'zzz', ], ],
读写分离怎么触发?Db::connect() 和 ->master() 有什么区别?
TP6 默认只在 INSERT/UPDATE/DELETE 时自动走主库,SELECT 默认轮询从库——但前提是没手动干预连接对象。一旦你调用 Db::connect('slave'),就等于绕过内置路由,后续所有操作都固定走那个从库,不再负载均衡。
立即学习“PHP免费学习笔记(深入)”;
容易踩的坑:在事务中混用读写,比如先 Db::transaction(function () { ... Db::table('log')->select(); ... });,此时 select() 仍可能被发到从库,导致事务一致性断裂(MySQL 从库默认不支持事务内读写混合)。
- 强制走主库查数据:用
Db::name('user')->master(true)->select() - 强制走某从库:不推荐,但可
Db::connect(['hostname' => 'slave1.example.com', ...])->table('user')->select() - 想让某个
SELECT也走主库(比如刚写完立刻读),必须显式加->master(true),别指望“刚写完会自动同步” - 关联查询(
with())默认主库 + 从库混合走,但如果关联表跨库或主从延迟高,结果可能不一致
从库延迟导致数据查不到,怎么兜底?
TP6 没内置延迟检测或自动切回主库机制。所谓“强一致性读”,得自己控制:对时效性敏感的读操作(如用户刚提交订单后查订单状态),一律加 ->master(true)。
更现实的做法是分层处理:普通列表页走从库,详情页 ID 查询加缓存+主库兜底。别幻想靠配置项解决延迟问题。
- 不要依赖
slave_balance(TP6 不支持该配置),轮询策略是硬编码在thinkdbconnectorMysql里的,无法插件化替换 - 监控从库延迟:需单独执行
SHOW SLAVE STATUS,TP6 不提供钩子自动采集 - 如果业务允许几秒延迟,可在从库查询前加
sleep(0.1)等待同步(仅测试环境,生产禁用) - 真正可靠的方案是应用层加缓存,比如用 redis 存刚写入的记录,查缓存命中则不查 DB
多从库下连接数爆了怎么办?
TP6 每个从库配置会独立维护连接池,默认最大连接数由 params' => [PDO::MYSQL_ATTR_MAX_BUFFER_SIZE] 之类控制不了,实际取决于 PHP-FPM 的 pm.max_children 和 MySQL 侧的 max_connections。
现象:上线后 MySQL 报 Too many connections,但 show processlist 显示大量 Sleep 状态连接来自不同从库地址。
- 给每个从库配置加
'break_reconnect' => true,断连时主动释放资源,而不是等超时 - 限制从库数量,TP6 对超过 3 个从库的轮询性能无优化,反而增加连接管理开销
- 确认是否误开了
'debug' => true,开启调试后每条 SQL 都会额外建连接做日志记录 - 用
Db::close()主动关闭不用的从库连接,尤其在长生命周期的 CLI 命令中
主从分离不是银弹,配置只是第一步。真正卡住人的,永远是主从延迟、连接泄漏、事务边界和缓存一致性——这些没法靠改几个数组键解决。