thinkphp的failover仅在连接建立阶段生效,主库已连上后查询失败不会触发切换;必须配置'deploy'=>1、'rw_separate'=>true、'master_num'=>1,并将从库独立配置于'slave'数组中。

ThinkPHP 连接 MySQL 主从时 failover 不生效?
默认配置下,ThinkPHP 的数据库故障转移(failover)根本不会触发——它只在「连接建立阶段」失败时才尝试切换,而主库已连上、后续查询报错(比如主库突然挂了),failover 完全不接管。
常见错误现象:SQLSTATE[HY000] [2006] MySQL server has gone away 或 Connection refused 后直接抛异常,备库完全没被用上。
- 必须显式开启
'deploy' => 1和'rw_separate' => true,否则 failover 逻辑压根不加载 -
'master_num' => 1是硬性要求,哪怕你只有一个主库,也得写上,否则读写分离判断失效,failover 被跳过 - 备库地址不能和主库写在同一组
'hostname'里,必须拆成独立的'slave' => [...]数组,且每个 slave 要有完整配置(hostname、username、password等)
手动触发主库切换要改哪几个配置项
ThinkPHP 不支持运行时自动探测主库宕机并切走,所谓“自动”只是连接池初始化时的一次性选择。真要实现查询中切换,得自己兜底。
使用场景:主库在事务中途断开、或长连接空闲超时后首次查询失败。
- 在
Db::execute()或Db::query()外层加try/catch,捕获PDOException或think\db\exception\DataNotFoundException - 捕获到连接类错误后,调用
Db::connect(['deploy' => 1, 'rw_separate' => true], true)强制重建连接实例(第二个参数true表示强制重新初始化) - 注意:
Db::connect()返回的是新实例,原模型或查询构造器不会自动继承,需重新赋值或用闭包封装
为什么 ping 检测不解决实际问题
ThinkPHP 的 break_reconnect 默认为 false,意味着即使设置了 'check_time' => 30,也不会定期 ping 主库;就算开了,ping 只检查 TCP 连通性,不验证 MySQL 服务是否可执行 SQL。
典型坑:mysqld 进程卡死但端口仍监听,ping 成功,后续查询照样失败,failover 却不会启动。
- 真正有效的做法是:在关键业务查询前,执行一条轻量
SELECT 1,并设较短超时(如'params' => [\PDO::ATTR_TIMEOUT => 2]) - 把该检查逻辑封装进自定义数据库中间件,避免每个
Db::table()->select()都重复写 - 别依赖
break_reconnect做故障恢复,它只管重连,不管换库
备库变主库后写操作怎么处理
ThinkPHP 的 failover 机制只管「读连接」,不改写路由规则。一旦你手动把备库升为主库,所有 INSERT/UPDATE/DELETE 仍会发往原主库地址(已不可用),除非你主动修改配置。
这意味着:故障转移不是高可用,只是连接容错;真正的主备切换需要外部协调(如 MHA、Orchestrator)或应用层配合。
- 不要在应用内做主库地址热更新,容易引发连接池混乱和事务不一致
- 如果必须临时写备库,用
Db::connect(['hostname' => 'new-master-ip'])显式指定,并确保该连接不被复用到其他逻辑 - 最稳妥的方式仍是让 DBA 触发 VIP 切换或 DNS 更新,让应用无感感知新主库地址
复杂点在于:ThinkPHP 的连接抽象层把「连接建立」和「SQL 执行」绑得太紧,failover 只发生在前者。很多开发者以为配好 slave 就万事大吉,结果线上主库一抖,整个写链路就断了。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











