thinkphp 6.0读写分离需同时满足deploy=1、rw_separate=true、read为二维数组、write为单维数组;否则查询静默走主库,且事务、锁、上一条为写操作等场景强制回主库。

ThinkPHP 6.0 的读写分离不是“配了 read 就自动分流”,必须同时满足四个硬性条件:deploy 为 1、rw_separate 为 true、read 是二维数组、write 是单维数组——缺一不可,否则所有查询静默走主库。
为什么 Db::name('user')->select() 还是连主库
TP6 不解析 SQL,而是靠方法语义 + 上下文联合判断路由。以下情况会强制回主库:
- 当前处于事务中(
Db::startTrans()后任何查询都走主库) - 查询带锁:
->lock(true)或原生 SQL 含FOR UPDATE/LOCK IN SHARE MODE - 上一条执行的是写操作(如
insert()),且未显式调用useReadConnection() -
value()、column()等方法在某些 TP6.x 小版本中被误判为写意图,需结合日志确认实际连接
config/database.php 的 read 和 write 必须这样写
结构错误是失效最常见原因。TP6 对数组维度极其敏感:
-
read必须是二维数组,哪怕只配一个从库也要写成[['hostname' => '192.168.1.11']],写成['hostname' => '192.168.1.11']会静默失败 -
write必须是单维关联数组,写成[['hostname' => '192.168.1.10']]会直接抛出Invalid write database config -
deploy必须设为1(整型),设成字符串'1'或布尔true均无效 -
default配置项必须指向你定义的连接名(如'mysql'),不能留空或指向不存在的键
Db::connect() 和 Db::name() 完全不同路
这两个方法根本不在同一套路由机制里:
-
Db::name('user')走的是default所指向的连接配置(如connections.mysql),是否分流取决于该配置里的rw_separate和read/write -
Db::connect('slave1')是手动创建全新连接实例,它只认自己配置里的hostname,完全不参与读写分离逻辑,也不会轮询、不会 fallback - 想临时切从库又不想改全局?只能用
Db::$connection->config(['read' => [['hostname' => 'xxx']]])动态重设,没有->slave()这种链式方法
真正麻烦的不是配置本身,而是主从延迟和事务穿透——TP6 不做健康检测、不跳过宕机从库、不提供强一致性读开关。一旦业务要求“刚写完立刻能读到”,就必须在代码里显式加 ->master() 或绕过读写分离逻辑,这点容易被忽略,直到线上查不到最新数据才暴露。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











