thinkphp6读写分离需同时满足'deploy'=>1、'rw_separate'=>true、'read'为非空数组三条件;db::connect()指定独立连接不参与分离路由,db::name()则依赖default配置;强制主库读须用db::master()->table()->find()链式调用或置于事务中。

ThinkPHP 的读写分离不是“配了就能用”,而是依赖明确的配置开关和调用方式。主库强制读也不是加个方法就生效,关键在作用范围和链式调用是否延续。
读写分离必须显式启用
仅在 database.php 里写上多个数据库地址或 'read' 数组,不会自动触发分离。TP6 要求同时满足三个条件:
- 'deploy' => 1:开启多服务器部署模式,这是总闸门
- 'rw_separate' => true:启用读写自动分离逻辑
-
'read' 配置非空且为数组:例如
'read' => [['host' => '192.168.1.10']],不能是[]、null或对象
缺一不可。漏掉 'deploy' => 1,整个读写分离模块根本不会初始化。
Db::connect() 与默认连接的区别
Db::connect('mysql') 是显式指定一个已命名的连接配置(需在 database.php 的 connections 下定义),它独立于全局默认行为,不参与读写分离路由判断。
Db::name('user') 或 Db::table('user') 则始终走 default 所指向的连接配置,是否走从库,完全取决于该配置中 'rw_separate' 是否开启、'read' 是否有效。
常见误区:以为 Db::connect('slave_read') 后再调用 ->table('user')->select() 就能复用该连接——其实不行,->table() 会重新绑定默认连接,必须写成 Db::connect('slave_read')->table('user')->select()。
强制主库读的两种可靠方式
不是所有“想读主库”的场景都适合同一种写法,得看上下文:
-
单次查询强一致:用
Db::master()->table('user')->where('id', 1)->find(),master()必须在链式开头,且后续所有操作(where、find)都在同一实例上调用 -
事务内读写混合:无需额外指定,事务中所有操作(包括 SELECT)默认走主库,
Db::transaction(function () { Db::table('user')->find(); })已足够
错误写法:Db::master(); Db::table('user')->find() —— 第一个 master() 返回新实例但没被使用,第二个 Db::table() 仍是默认连接。
调试读写分离是否真生效
getLastSql() 显示的只是 SQL 字符串,不体现执行位置。靠它判断走主还是从,100%误判。
真正有效的验证方式只有两个:
- 在从库执行
SHOW PROCESSLIST,过滤应用服务器 IP,确认是否有来自 PHP 的 SELECT 连接 - 在
think/db/Connection.php的connect()方法中加日志,打印$config['hostname']和$isRead标志
如果从库进程列表里始终看不到 SELECT,优先检查 'read' 是否为空、'deploy' 是否遗漏、以及是否意外进入了事务或用了锁语句(如 lock(true))。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











