主从配置需同时启用deploy=1和rw_separate=true才生效;否则退化为单库操作。强制走从库用db('slave')或->master(false);事务中->master(false)无效;故障转移需手动兜底;读写分离下lastinsertid()后查询须显式->master(true)。

主从配置写对了但不生效?检查 database.php 的 deploy 和 rw_separate
ThinkPHP 5.1+ 的主从读写分离不是“配了就自动切”,必须显式开启部署模式和读写分离开关。常见现象是:明明写了多个数据库配置,Db::table('user')->select() 却始终走主库,查日志也看不到从库连接记录。
关键配置项只有两个:
-
deploy => 1:启用分布式部署(主从/集群的基础开关) -
rw_separate => true:开启读写分离(注意不是rw_separate => 1,布尔值写错会导致静默失效)
其他如 master_num、slave_no 属于进阶控制,非必需。漏掉 deploy 或类型写错,整个主从逻辑直接跳过,TP 会退化为单库操作。
如何强制走从库?用 db('slave') 或 ->master(false)
默认情况下,SELECT 语句由 TP 自动路由到从库,但这个行为不可见、难验证。演练时需要确定性触发从库访问,避免“以为走了从库,其实没走”。
-
db('slave'):直连指定的从库配置(需在database.php中定义名为slave的独立配置) -
Db::table('user')->master(false)->select():强制禁用主库,走从库池(配合slave_count轮询) -
Db::connect(['dsn' => 'mysql:host=192.168.10.22;dbname=test']):临时绕过配置,直连特定 IP 验证网络与权限
注意:->master(false) 在事务中会被忽略——TP 认为事务内所有操作必须在同一节点,这是合理限制,但容易被忽略导致演练误判。
故障转移怎么测?关主库后看 Connection refused 是否被拦截
真正的故障转移不是“主挂了自动切”,而是“主不可达时,读操作降级走从库,写操作抛出可捕获异常”。演练重点不是等它自动恢复,而是确认失败路径是否可控。
- 停掉主库 MySQL 服务(
systemctl stop mysqld或杀进程),再执行Db::table('user')->insert([...]) - 预期结果:抛出
PDOException,错误信息含Connection refused或Can't connect to MySQL server - 此时若仍想让读请求继续工作,需在应用层兜底,例如:
try {
$data = Db::table('user')->select();
} catch (\PDOException $e) {
if (strpos($e->getMessage(), 'Connection refused') !== false) {
$data = db('slave')->table('user')->select(); // 手动切从库
}
}
TP 本身不提供全自动故障转移(即写操作 fallback 到从库),那是违反数据一致性的,别被“高可用”宣传误导。
从库延迟导致数据不一致?lastInsertId() 和 find() 必须走主库
演练中常犯的错:在插入后立刻用 select 查刚写入的数据,却没意识到从库可能延迟几秒,查不到或查到旧值。
-
Db::table('user')->insertGetId()返回的是主库 ID,但后续Db::table('user')->find($id)默认走从库 → 可能查不到 - 解决方案:显式指定主库查询
Db::table('user')->master(true)->find($id) - 同理,
lastInsertId()、update、delete后的校验查询,都应加->master(true)
这不是 TP 的 bug,是主从架构固有约束。演练时不模拟延迟、不验证跨库一致性,等于白测。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











