thinkphp主从配置必须同时满足启用deploy、开启rw_separate、结构完整三条件,缺一即全部走默认主库;deploy须嵌套在connections具体连接内,read支持多从库数组,write仅允许单数组。

ThinkPHP 主从配置不是加个 read 数组就自动生效,必须同时满足三个硬性条件:启用部署开关、开启读写分离、主从配置结构完整;漏掉任一,Db::name() 全部走默认连接(即主库),根本不会分流。
database.php 里 deploy => 1 是启动读写分离的总闸门
TP6 不会主动扫描 read 或 write 配置——只有 'deploy' => 1 存在且为真,整个读写分离逻辑才被加载。它和 'rw_separate' => true 是父子关系:deploy 关不掉,rw_separate 就没机会起作用。
常见错误:
-
default指向一个连接名(如'mysql'),但'mysql'配置里没写'deploy' => 1→ 全部查询走主库,read配置完全被忽略 - 把
deploy写在default顶层(如'default' => ['deploy' => 1, 'type' => 'mysql'])→ TP 启动报错或静默降级
正确位置:必须嵌套在具体连接配置内部,例如:
'connections' => [
'mysql' => [
'deploy' => 1,
'rw_separate' => true,
'read' => [['hostname' => 'slave1'], ['hostname' => 'slave2']],
'write' => ['hostname' => 'master'],
// 其他通用参数(type/database/username等)
]
]
read 和 write 的结构差异极易踩坑
read 支持数组套数组(多个从库),TP 会随机选一个;write 只允许单个数组(一个主库),写成数组直接触发 Invalid write database config 异常。
典型错误场景:
-
'write' => [['hostname' => 'master']]→ 报错,必须是['hostname' => 'master'] -
'read' => ['hostname' => 'slave1']→ 看似合法,但 TP 会把它当做一个“单元素从库列表”,而非“单个从库配置”,实际仍可运行,但语义模糊、易混淆 - 从库之间用户名/密码/数据库名不同 → 每个
read子数组必须各自带全参数,不能只补hostname
安全写法:
'read' => [
[
'hostname' => 'slave1.example.com',
'database' => 'app_db',
'username' => 'ro_user',
'password' => 'xxx'
],
[
'hostname' => 'slave2.example.com',
'database' => 'app_db',
'username' => 'ro_user',
'password' => 'yyy'
]
]
哪些查询会绕过从库、强制走主库
TP6 的路由判断基于 SQL 语义和执行上下文,并非所有 SELECT 都进从库。以下情况一律走主库:
- 显式加锁:
->lock(true)、->lock('FOR UPDATE') - 原生 SQL 包含
FOR UPDATE或LOCK IN SHARE MODE - 当前处于事务中(哪怕只是
Db::startTrans()后的普通select()) - 调用了
->master()方法(如Db::name('user')->master()->select())
注意:with() 关联查询默认混合走主+从,但如果关联表跨库或主从延迟高,结果可能不一致——这不是 TP 的 bug,而是架构层面的权衡。
Db::connect() 和 Db::name() 的行为边界
Db::name('user') 永远绑定 default 所指向的连接配置(比如 'mysql'),它的读写路由完全由该配置里的 'rw_separate' 控制;而 Db::connect('mysql_slave') 是绕过内置路由的“直连模式”,后续所有操作固定走那个指定连接,不再参与负载均衡或自动分离。
所以:
- 想临时查某个特定从库(比如做数据校验),用
Db::connect('xxx'),但要自己管理连接定义 - 想让某次读操作也走主库(刚写完立刻强一致性读),必须加
->master(true),别指望延迟同步兜底 - 不要在模型里硬编码
Db::connect(),否则测试和切换环境时极难维护
最易被忽略的一点:主从延迟是业务层必须面对的事实。TP 不提供延迟感知或自动重试机制,insert 后立刻 select,如果没加 ->master(true),大概率读到旧数据。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











