ThinkPHP主从配置后SELECT仍走主库,因默认链式查询强制走主库以保障事务一致性;需显式调用master(false)或启用read_master配置才能触发从库路由。

ThinkPHP 主从配置后 SELECT 仍走主库?
不是配置没生效,而是 ThinkPHP 默认只在显式调用 db()->query() 或使用 db('table')->master(false) 时才触发从库路由。普通 select()、get() 等链式调用默认强制走主库——这是为事务一致性做的保守设计,但常被误认为“读写分离失效”。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 确认是否在非事务上下文中使用读操作;若在
Db::transaction()内,所有查询都会被绑定到主库,无法切从 - 手动指定从库:用
db('user')->master(false)->select(),master(false)是关键开关 - 全局开启自动读写分离需在数据库配置中设
'read_master' => true(TP6.0+),否则即使配了从库,ORM 层也不会自动分流 - 检查
database.php中'hostname'是否仍写死为主库地址,而'slave' => ['hostname' => 'xxx']没有正确嵌套
InvalidArgumentException: Database config "slave" not found
这个错误说明 ThinkPHP 在解析从库配置时找不到 slave 键,常见于 TP6.1+ 的多级配置结构变更:旧版支持平铺式 'slave' => [...],新版要求从库必须放在 'connections' => ['mysql' => [...]] 下的 'slave' => [...] 子数组中,且需与主库同级。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- TP6.0+ 正确结构是:
'connections' => ['mysql' => ['type' => 'mysql', 'hostname' => 'master-ip', 'slave' => [['hostname' => 'slave1-ip'], ['hostname' => 'slave2-ip']]] - 如果用了多应用模式,确保配置加载顺序:从
config/database.php加载,而非被应用层的extra配置覆盖 - 运行
php think clear:config清除配置缓存,避免旧配置残留
主从延迟导致 INSERT 后立刻 SELECT 查不到数据
这不是 ThinkPHP 的 Bug,而是 MySQL 主从复制本身的最终一致性限制。写主库后立即读从库,可能因 binlog 传输或 SQL 线程延迟,查不到刚插入的记录。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 对强一致性场景(如注册后跳转个人页),直接加
->master(true)强制读主库:db('user')->master(true)->where('id', $id)->find() - 避免在同一个请求中混合“写主 + 读从”,尤其涉及自增 ID 或唯一键判断时
- 监控从库延迟:用
SHOW SLAVE STATUS\G查Seconds_Behind_Master,长期 > 1s 需优化网络或从库负载 - TP6 不提供“写后自动切主读”的中间件,需业务层自行控制,不能依赖框架自动兜底
分布式部署下从库连接随机失败
当多个 PHP-FPM 进程共用同一组从库配置,且从库未启用连接池或最大连接数过低时,容易出现 Too many connections 或 Connection refused,尤其在高并发短连接场景下。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 从库 MySQL 的
max_connections至少设为主库的 2–3 倍,并确认wait_timeout不低于 PHP 的max_execution_time - TP6 默认不复用 PDO 连接,每次查询新建连接;如需复用,可在配置中加
'deploy' => 1并启用'pool_size' => 10(仅限 6.1+) - 不要把从库 IP 写成内网 DNS 名(如
mysql-slave.local),FPM 进程多时 DNS 缓存可能不一致,优先用固定 IP - 健康检查不能只靠连接成功,要执行
SELECT 1并校验返回,否则网络抖动后连接句柄可能卡在半开状态
主从切换本身不难,难的是识别哪一层在“假失败”:是配置结构错、缓存没清、还是复制延迟被当成程序错误。线上出问题先抓 Db::getLastSql() 和 show processlist,比反复改配置更快。



















