thinkphp 6 读写分离需同时满足 deploy => 1、rw_separate => true,且 read 必须为数组套数组、write 为单数组,否则默认全走主库;查询带锁、在事务中或调用 ->master() 时强制走主库。

ThinkPHP 6 的读写分离不是“配了 read 就自动走从库”,必须同时满足 deploy => 1、rw_separate => true,且 read 和 write 配置结构正确,否则全走 default 连接——也就是主库。
database.php 里怎么写才算真正启用读写分离
很多人把 read 写成单个数组(比如 ['hostname' => '192.168.1.10']),结果 TP 启动就报错或静默失效。TP6 要求 read 必须是「数组套数组」,哪怕只配一个从库也要写成 [0 => ['hostname' => '192.168.1.10']];write 只能是单个完整连接数组,不能是数组套数组,否则直接抛出 Invalid write database config。
关键点在于:整个读写分离逻辑由 connections.mysql 下的配置驱动,而 default 只是用来指向这个配置名:
-
default设为'mysql'(字符串) -
connections.mysql下必须包含:'deploy' => 1、'rw_separate' => true、'read' => [[...]]、'write' => [...] - 漏掉
deploy => 1,整个读写分离模块根本不会初始化
为什么 Db::name('user')->select() 还是走主库
TP6 不是按 SQL 类型(如是否含 SELECT)判断路由,而是按方法语义 + 上下文联合判定。以下情况会强制走主库:
- 查询带锁:
->lock(true)或原生 SQL 含FOR UPDATE/LOCK IN SHARE MODE - 当前处于事务中:
Db::startTrans()后的所有查询,无论是否只读,全部回落到主库 - 调用了写操作连贯方法(如
->order()->limit()->count()),TP 有时会误判为写意图
纯读方法(find()、select()、value()、column())才可能进从库,前提是没触发上述任一条件。
Db::connect() 和 Db::name() 对读写分离的影响
Db::name('user') 完全依赖 default 所指向的连接配置(即 connections.mysql)是否开启 rw_separate;而 Db::connect('xxx') 是创建全新连接实例,它只认自己配置里的 hostname,完全不参与 read/write 路由逻辑。
也就是说:
- 想用读写分离 → 必须走
Db::name()(或Db::table())+ 正确的connections.mysql配置 - 用了
Db::connect('slave1')→ 等于手动绕过读写分离机制,该连接永远只连指定地址 - 临时切从库又不想改全局?只能通过
Db::$connection->config(['read' => [...]])动态重设,没有->slave()这种链式方法
从库延迟时业务层怎么兜底
TP6 不做延迟探测、不自动故障转移、不支持权重轮询——它只是随机选一个 read 数组元素去连。一旦从库同步滞后,save() 后立刻 find() 可能查不到数据,这是框架明确声明的「需业务层兜底」场景。
常见应对方式:
- 强一致性读:对刚写入就查的场景,显式加
->master(),例如Db::name('user')->master()->where('id', $id)->find() - 缓存兜底:写后更新 Redis 缓存,读优先走缓存,避免直击从库
- 延迟容忍提示:前端展示「数据同步中」状态,配合定时轮询
别指望 TP 自动识别主从延迟并切换连接——它连 SHOW SLAVE STATUS 都不执行。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











