ThinkPHP实现故障切换的关键是数据库配置必须指向Keepalived提供的VIP(如192.168.10.200),而非具体节点IP;VIP漂移仅解决网络层切换,需配合双主复制健康、自检脚本及应用层重试机制才能真正高可用。

ThinkPHP 本身不感知 Keepalived,也不参与 VIP 漂移或主库切换逻辑——它只认一个数据库连接地址。所以「ThinkPHP 中实现故障切换」的本质,是让它的 database.php 配置始终指向当前活跃的主库,而这个「活跃主库」由 Keepalived 通过虚拟 IP(VIP)对外统一暴露。
ThinkPHP 的数据库配置必须用 VIP,不能写具体节点 IP
如果你在 config/database.php 里写了 'hostname' => '192.168.10.120' 或 'hostname' => '192.168.10.125',那 Keepalived 就完全失效了:应用会直连某台物理机,绕过 VIP 调度,故障时无法自动切走。
正确做法是:
- Keepalived 配置中定义好 VIP,例如
192.168.10.200 - 所有 ThinkPHP 实例的
'hostname'必须设为该 VIP,如'hostname' => '192.168.10.200' - 确保应用服务器能路由到该 VIP(同网段、无防火墙拦截、ARP 缓存及时刷新)
MySQL 主主复制状态必须稳定,否则 VIP 切换后写入会失败
Keepalived 只管网络层漂移,不管数据层一致性。如果两台 MySQL 节点间复制已中断,VIP 漂到从节点后,应用发起写操作,可能因 SHOW SLAVE STATUS 中 Seconds_Behind_Master > 0 或 Slave_IO_Running = No 导致新主库缺失最新数据,进而引发主键冲突、唯一索引报错甚至数据覆盖。
立即学习“PHP免费学习笔记(深入)”;
关键检查点:
- 双主双方都需开启
log_slave_updates = ON,否则互为主从无法闭环 - 每个节点的
server-id必须唯一,且不能为 0 或重复 - 建表时强制使用
AUTO_INCREMENT步长错开,例如节点1设auto_increment_offset = 1、auto_increment_increment = 2;节点2反过来 - 务必部署
check_mysql.sh类健康脚本,由 Keepalived 的vrrp_script调用,只在mysql -h127.0.0.1 -e "SELECT 1"+SHOW SLAVE STATUS均成功时才认为节点可接管 VIP
应用层无感知切换的前提:连接池与重试机制要配合
VIP 漂移不是原子操作,中间存在几秒窗口期:旧主断连 → Keepalived 检测超时 → VIP 绑定到新主 → ARP 广播更新 → 应用 TCP 连接重建。这期间已有连接会报错,比如 SQLSTATE[HY000] [2002] Connection refused 或 Lost connection to MySQL server during query。
ThinkPHP 默认不自动重试,你需要主动处理:
- 在
think\db\Connection异常捕获中,对上述网络类错误做有限次重试(建议 ≤3 次),避免雪崩 - 禁用长连接(
'break_reconnect' => true),防止复用已失效的 socket - 若使用 PDO,确认
PDO::ATTR_ERRMODE设为PDO::ERRMODE_EXCEPTION,否则错误静默吞掉 - 不要依赖 Laravel 风格的「重连钩子」,ThinkPHP 6.x 的
onBreak回调仅触发不自动恢复,仍需手动$this->close()+$this->query()
最易被忽略的一点:VIP 切换后,原主库恢复上线时,必须人工确认复制状态并执行 START SLAVE,否则它会以「旧主」身份继续拒绝同步请求;而 Keepalived 不会、也不能替你修复 MySQL 复制链路——它只负责把流量导给「当前它认为健康的那个 IP」。



















