ThinkPHP连接HAProxy代理的MySQL需将DB_HOST设为HAProxy地址(如192.168.1.100),端口对应其监听端(如3306写/3307读),由应用层显式控制读写连接,HAProxy仅做TCP透明转发。

ThinkPHP怎么连HAProxy代理的MySQL
ThinkPHP 本身不感知数据库是否走 HAProxy,它只按标准 MySQL 连接参数发起连接。关键在于把 DB_HOST 指向 HAProxy 的监听地址(比如 192.168.1.100),而不是直接写主库 IP。HAProxy 在中间做 TCP 层转发或 HTTP 健康探活,对 ThinkPHP 完全透明。
常见错误现象:
-
SQLSTATE[HY000] [2002] Connection refused:HAProxy 未启动、端口没监听、防火墙拦截、或后端 MySQL 实例全部check失败被剔除 - 写操作失败但读正常:HAProxy 配置了
option httpchk但健康接口没返回 200,或后端只配置了从库检查却误将写请求也发过去
实操建议:
- 确保 HAProxy 使用
mode tcp(不是http)代理 MySQL,默认端口3306,避免协议解析开销 - 在 ThinkPHP 的
.env中设置:DB_HOST=192.168.1.100(HAProxy VIP)、DB_PORT=3306,其他参数不变 - 禁用 ThinkPHP 的长连接(
'params' => [\PDO::ATTR_PERSISTENT => false]),避免连接复用导致故障节点残留 - 测试时用
mysql -h 192.168.1.100 -P 3306 -u xxx -p直连验证,绕过框架确认链路通
HAProxy backend怎么区分读写流量
HAProxy 本身不解析 SQL,无法自动拆分读写。想实现读写分离,必须由 ThinkPHP 应用层控制——比如用 Db::connect('read') 显式指定从库连接,或借助中间件/自定义连接池路由到不同 HAProxy backend。
立即学习“PHP免费学习笔记(深入)”;
典型做法是部署两套 HAProxy backend:
- 一个
backend mysql_write只指向主库(server master 192.168.1.201:3306 check) - 另一个
backend mysql_read指向多个从库(server slave1 192.168.1.202:3306 check,server slave2 192.168.1.203:3306 check)
然后在 ThinkPHP 配置中定义两个数据库连接名:
DB_CONNECTION_WRITE=mysql DB_HOST_WRITE=192.168.1.100 DB_PORT_WRITE=3306 DB_CONNECTION_READ=mysql DB_HOST_READ=192.168.1.100 DB_PORT_READ=3307
再让 HAProxy 监听两个端口:bind *:3306 转发到 mysql_write,bind *:3307 转发到 mysql_read。这样应用只需切换连接名,HAProxy 就自动走不同后端。
为什么 HAProxy 的 health check 要配 httpchk 而不是 tcp-check
MySQL 协议本身没有标准健康响应,tcp-check 只能确认端口通、TCP 握手成功,但无法判断 MySQL 进程是否真能执行查询(比如主库已卡死、复制中断、OOM 后僵死)。而 httpchk 需要你写一个轻量级健康接口(如 /health),在其中执行 SELECT 1 并校验结果,这才是真正可用的检测。
实操建议:
- 不要依赖
mysqladmin ping或端口探测,它们太浅层 - 健康接口必须查表(哪怕
SELECT 1 FROM DUAL),且超时设为2s内,避免拖慢 HAProxy 调度 - 如果不用 HTTP 方式,可改用
tcp-check+ 自定义脚本(如mysql -e "SELECT 1" &>/dev/null),但需在每台 MySQL 上部署并开放本地执行权限,运维成本更高
ThinkPHP runtime 目录和 session 存 Redis 是高可用的前提
多实例下,runtime 目录若仍写本地磁盘,缓存、日志、模板编译都会冲突或丢失;Session 若用文件驱动,用户刷新就掉登录。这两项不共享,HAProxy 再稳也没用。
必须做的配置:
- 关闭
app_debug,避免runtime生成调试信息干扰 - 把
runtime改为 Redis 缓存驱动:'type' => 'redis',并确保所有实例共用同一 Redis 实例或集群 - Session 改用 Redis:
'type' => 'redis',配置'host' => '192.168.1.150'(指向 Redis VIP 或集群入口) - 上传目录不能放本地,必须走 OSS/NFS,否则图片上传后其他实例读不到
最容易被忽略的是:Redis 本身也要高可用。如果只配单点 Redis,那它一挂,整个 ThinkPHP 集群的 Session 和缓存就全崩——得上 Redis Sentinel 或集群模式,且 ThinkPHP 的 Redis 配置里要填多个节点或启用哨兵发现。



















