mysql连接超时需分层治理:php端用mysqli_options设连接超时、mysqli_ping检测并重连;pdo需用stream_context控制底层socket超时;服务端wait_timeout针对非交互连接,调大治标不治本;proxysql等中间件引入后须统一各层超时设置。

MySQL 连接超时被 mysqli_connect() 拒绝
PHP 默认用 mysqli_connect() 建连时,如果 MySQL 服务端已关闭连接(比如 wait_timeout=60),而 PHP 客户端没做复用检测,就会直接报错:MySQL server has gone away 或 Connection refused。这不是 PHP 代码写错了,是连接池“睡过头”了。
解决思路不是拼命调大服务端超时,而是让 PHP 主动管好自己的连接:
-
mysqli_options($link, MYSQLI_OPT_CONNECT_TIMEOUT, 5)控制建连阶段最多等几秒(仅限 TCP 握手+认证) - 每次执行查询前,用
mysqli_ping($link)检查连接是否还活着;失败就mysqli_real_connect()重连 - 别在 long-running CLI 脚本里复用一个
$link变量跑一整晚——它大概率早断了
PHP-FPM 下 PDO::ATTR_TIMEOUT 不生效?
这个配置项只控制 PDO 内部执行 SQL 的等待时间(比如锁表卡住),**不控制连接建立或网络层超时**。很多人设了 PDO::ATTR_TIMEOUT => 3 却发现连不上数据库时还是卡 30 秒,就是因为底层 socket 连接本身没设 timeout。
正确做法是绕过 PDO 的封装,手动控制底层流:
立即学习“PHP免费学习笔记(深入)”;
- 创建 PDO 时传入
array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4')是安全的,但别指望它改超时 - 真要控连接超时,得用
stream_context_create(['socket' => ['timeout' => 3]])配合PDO::__construct()的 DSN 中指定unix_socket=或强制走 TCP - 更稳的方式:把数据库连接封装成工厂函数,每次请求都新建连接(PHP-FPM 模型下开销可接受)
MySQL 服务端 wait_timeout 和 interactive_timeout 区别
这两个值决定空闲连接多久被 MySQL 主动干掉。wait_timeout 作用于非交互式连接(比如 PHP 的 mysqli/pdo),interactive_timeout 才管 mysql 命令行这种带 --interactive 的会话。PHP 几乎总是走 wait_timeout。
调大它能缓解问题,但治标不治本:
- 设成
28800(8 小时)看似保险,其实掩盖了连接泄漏——某次查询忘了mysqli_free_result(),结果集一直占着连接不放 - 线上环境建议保持默认
28800或600(10 分钟),逼自己写健壮逻辑;开发环境可以设小点(如60)快速暴露问题 - 改完记得
mysql> SHOW VARIABLES LIKE '%timeout%';确认生效,有些云数据库控制台改了不 reload,得重启实例
连接池中间件(如 ProxySQL)介入后超时链路变复杂
加了 ProxySQL 或 MySQL Router 后,超时不再只是 PHP↔MySQL 两段,而是变成 PHP↔ProxySQL↔MySQL 三段。每段都有自己的 timeout 设置,容易互相打架。
典型症状:PHP 报 Connection timed out,但查 ProxySQL 日志发现它早就把请求转发给后端 MySQL 了——说明是 PHP 到 ProxySQL 这段卡住了。
- ProxySQL 的
mysql-monitor_writer_is_also_reader等配置会影响健康检查频率,间接拖慢连接建立 - 必须统一设置:PHP 的 connect timeout ≤ ProxySQL 的
connect_timeout_server≤ MySQL 的connect_timeout - 最省事的调试法:用
mysql -h proxysql_host -P 6033 -u user -p手动连 ProxySQL,看是否卡顿;再用telnet proxysql_host 6033测基础连通性
$pdo 变量,在 FPM worker 生命周期里可能复用了几十次请求,而没人检查它是不是还在呼吸。











