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