PHP数据库连接超时需先定位环节:无“start connect”日志为连接建立超时,有日志无查询日志为握手卡住,有查询日志无结束为SQL执行阻塞;mysqli须用mysqli_init()+mysqli_options()在real_connect前设MYSQLI_OPT_CONNECT_TIMEOUT;PDO需DSN中加connect_timeout且连接后设ATTR_TIMEOUT,禁用持久连接;全链路须设set_time_limit并禁用0值。

PHP8数据库连接超时不是等它卡住再排查,而是要先确认是连接阶段超时、查询阶段阻塞,还是脚本整体被掐断——三者触发点不同、修复路径完全不同,改错地方只会让问题更隐蔽。
第一步:确认超时发生在哪个环节
在数据库操作前加一行日志:error_log('[DB] start connect at ' . date('Y-m-d H:i:s') . PHP_EOL, 3, '/tmp/db_debug.log');;执行后检查日志里有没有对应的「start connect」记录。没有→是连接建立失败或超时;有但后续无查询日志→卡在连接握手;有查询日志但没结束→问题出在SQL执行或结果集处理。
这一步不能跳过。很多开发者直接去调set_time_limit(),结果发现连接根本没建上,改了也白搭。
MySQLi连接超时设置(推荐用于原生或轻量项目)
方法一:连接时指定超时参数
立即学习“PHP免费学习笔记(深入)”;
创建连接前,用mysqli_init()初始化对象,再调用options()设值:
$mysqli = mysqli_init();→mysqli_options($mysqli, MYSQLI_OPT_CONNECT_TIMEOUT, 3);→mysqli_options($mysqli, MYSQLI_OPT_READ_TIMEOUT, 8);→mysqli_options($mysqli, MYSQLI_OPT_WRITE_TIMEOUT, 8);→$mysqli->real_connect('127.0.0.1', 'root', '', 'test_db', 3306);
注意:【MYSQLI_OPT_CONNECT_TIMEOUT必须在real_connect()之前调用,否则无效】。PHP 8.0+ 已废弃mysqli_connect()的超时参数,硬传会被忽略。
方法二:连接成功后动态调整会话级超时
执行$mysqli->query("SET SESSION wait_timeout = 15; SET SESSION interactive_timeout = 15;");。这个值只对当前连接生效,不影响MySQL全局配置,适合临时延长长查询容忍度。
PDO连接超时控制(ThinkPHP8等框架底层默认使用)
第一步:在DSN中加入connect_timeout参数
修改config/database.php中的dsn字段,例如:"mysql:host=127.0.0.1;port=3306;dbname=test_db;charset=utf8mb4;connect_timeout=3"。这个参数由PDO底层传递给MySQL驱动,控制TCP握手最大等待时间。
第二步:连接后立即设置PDO属性
在params数组中加入:PDO::ATTR_TIMEOUT => 5。它控制的是PDO内部等待服务器响应的时间,和DSN里的connect_timeout不重复也不冲突——前者管“连上”,后者管“连上后说话别卡”。
第三步:禁用持久连接(FPM环境必做)
在database.php中显式关闭:'persistent' => false,并从params中移除PDO::ATTR_PERSISTENT => true。PHP 8+ 的FPM子进程复用PDO连接极易导致Sleep连接堆积,表现为“连接数暴涨但实际并发不高”,本质是超时未释放。
全链路兜底:脚本级总时限控制
① 在入口文件index.php顶部加:set_time_limit(30);。它重置整个请求生命周期上限,防止某个死循环或阻塞IO拖垮整个脚本。
② 若使用CLI命令(如php think migrate),需单独处理:在命令类的handle()方法开头加set_time_limit(120);,因为CLI模式下默认是0(不限时),但部分Linux系统会受ulimit -t限制,必须显式设值才生效。
③ 禁用set_time_limit(0)。这不是“放开限制”,而是放弃控制权,一旦MySQL服务假死,PHP进程将永久挂起,Web服务器资源被持续占用。
验证是否真正修复
写一个独立测试脚本test_timeout.php放在网站根目录:
<?php$start = microtime(true);try { $pdo = new PDO("mysql:host=127.0.0.1;connect_timeout=2", "root", "", [PDO::ATTR_TIMEOUT => 3]); $pdo->exec("SELECT SLEEP(4)"); // 故意触发查询超时 echo "OK";} catch (PDOException $e) { echo "Caught: " . $e->getMessage();}echo "\nTime: " . round(microtime(true) - $start, 2) . "s";?>
浏览器访问该脚本,输出应为类似Caught: SQLSTATE[HY000]: General error: 2013 Lost connection to MySQL server during query且耗时约3秒左右。如果超过5秒才返回,说明PDO::ATTR_TIMEOUT未生效,需回头检查params是否被框架中间件覆盖。



















