ThinkPHP 5.0–5.1 读写分离需同时满足 read_master=true 且不在事务中、上一条非写操作才走从库;slave 配置必须一维扁平化,deploy=1 是前提;仅 find/select/get 等查询方法走从库,query/execute/关联查询默认不走,事务内强制主库。

ThinkPHP 5.0–5.1 的读写分离不是“自动识别 SQL 类型就切库”,它依赖两个硬性条件:显式开启 read_master,且当前不处于事务中、上一条操作也不是写语句——否则哪怕你配了从库,select 也会默默走主库。
为什么设了 read_master => true 却没走从库?
TP5.0–5.1 的从库路由逻辑极简,只看两个标记:
- 是否在事务中(
inTransaction()返回true) - 上一条执行的 SQL 是否为写操作(靠内部变量
lastInsID或lastSql判断)
只要其中任一为真,后续所有 find()、select() 都强制回主库。常见踩坑场景:
- 刚执行过
insert(),紧接着查这条记录,以为加个slave()就行——但框架根本不理,因为写标记还在 - 用
Db::table()->where()->update()后立刻->select(),结果还是主库 -
read_master写在.env里,但配置文件没做env('READ_MASTER', false)显式读取,导致实际未生效
slave 配置必须是一维数组,不能嵌套
TP5.0–5.1 不支持从库列表写成二维结构,以下写法会静默失败或连错库:
立即学习“PHP免费学习笔记(深入)”;
'slave' => [
['host' => 's1'],
['host' => 's2']
]
正确写法是扁平化的一维数组,用逗号分隔多个地址(其他参数同理):
'hostname' => '192.168.1.10,192.168.1.11', 'username' => 'user1,user2', 'password' => 'pass1,pass2', 'hostport' => '3306,3306', 'deploy' => 1, 'rw_separate' => true, 'read_master' => true,
注意:deploy => 1 是启用分布式模式的前提,缺了它,rw_separate 和 read_master 全部无效。
哪些读操作会走从库?哪些不会?
TP5.0–5.1 只对明确属于「查询类」的模型/查询方法启用从库路由,其余一律走主库:
- ✅ 走从库:
find()、select()、get()、value()(仅限模型调用)、column() - ❌ 不走从库:
query()(原生 SQL 查询,默认主库)、execute()、insertAll()、update()等写方法,以及任何封装了execute()的自定义方法 - ⚠️ 关联查询(
with())默认不继承主模型的从库策略,关联表仍走各自配置的默认连接(通常是主库)
想让关联也走从库,必须显式指定:hasOne('Profile')->connection('slave'),且确保 'slave' 是你在 connections 里预定义的合法连接名。
事务内无法切从库,这是设计使然
一旦调用 Db::startTrans() 或模型中使用 transaction(),整个事务生命周期内所有查询(包括 find())都绑定到事务起始时获取的主库连接。
这时候加 ->slave() 会被忽略,强行调用会报错或返回空结果——不是 bug,是为保证「读己所写」的一致性。
真正需要减轻主库压力的读操作,应该拆到事务外再执行,并确保业务能容忍主从延迟。比如订单创建成功后,立即查用户积分统计这类非关键数据,才适合放事务外 + ->slave()。



















