You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程PHP脚本使用MySQL出现“MySQL server has gone away”错误排查

解决PHP parallel线程中MySQL连接“server has gone away”的问题

我之前帮朋友排查过几乎一模一样的场景,咱们先把核心矛盾捋清楚:你明明是在线程回调里新建的MySQL连接,怎么还会出现连接被断开的问题?答案其实藏在你线程的执行流程里——咱们一步步拆解:

可能的核心原因

你的线程逻辑是:新建MySQL连接 → 发送500条FCM推送 → 更新数据库状态。问题就出在「发送FCM推送」这一步:批量HTTP请求大概率会耗时不短(哪怕单条请求快,500条累计起来也可能有几分钟),这段时间里MySQL连接完全处于空闲状态。如果MySQL的wait_timeout设置得比这个耗时短(有些环境默认是几分钟,而非你以为的8小时),MySQL就会主动关闭这个长期空闲的连接。等你要执行UPDATE的时候,手里握着的已经是失效的连接,自然就抛出“server has gone away”错误了。

另外还有个小概率可能:parallel的线程环境存在隐性的资源残留,但这个可能性远低于「空闲连接被回收」。

针对性解决方案

1. 延迟创建数据库连接(最推荐)

既然问题是“连接创建后空闲太久”,那干脆把创建MySQL连接的时机延后——等FCM推送完全发送完成后,再新建连接更新状态。这样连接创建后立刻执行操作,几乎没有空闲时间,从根源上避免被回收:

// 线程回调优化后的逻辑
function processPushBatch($notifications) {
    // 第一步:先完成FCM推送发送
    sendFCMPush($notifications);
    
    // 第二步:推送完成后再新建MySQL连接
    $mysqli = new mysqli('你的主机', '用户名', '密码', '数据库名');
    if ($mysqli->connect_error) {
        die('连接失败: ' . $mysqli->connect_error);
    }
    
    // 执行状态更新
    $poolId = $notifications[0]['poolId'];
    $mysqli->query("UPDATE notifications SET status='sent' WHERE poolId='$poolId'");
    
    $mysqli->close();
}

2. 执行更新前检查并重建连接

如果没法调整连接创建时机,可以在执行UPDATE前先验证连接有效性,失效就重新初始化:

// 线程回调中的数据库操作部分
function updateNotificationStatus($poolId) {
    $mysqli = new mysqli('你的主机', '用户名', '密码', '数据库名');
    
    // 检查连接是否可用,无效则重建
    if (!$mysqli->ping()) {
        $mysqli->close();
        $mysqli = new mysqli('你的主机', '用户名', '密码', '数据库名');
    }
    
    // 执行更新
    $mysqli->query("UPDATE notifications SET status='sent' WHERE poolId='$poolId'");
    $mysqli->close();
}

ping()方法会快速向MySQL发送一个测试包,验证连接是否存活,无效就重新创建,能直接解决连接被回收的问题。

3. 调整MySQL的空闲超时参数

如果你的批量推送耗时确实较长(比如超过10分钟),可以适当调整MySQL的wait_timeout和interactive_timeout参数(这两个参数建议设成相同值)。比如如果单批次推送最长耗时15分钟,就把超时设成20分钟:

-- 临时生效,重启MySQL后失效
SET GLOBAL wait_timeout = 1200;
SET GLOBAL interactive_timeout = 1200;

要永久生效的话,修改MySQL配置文件(比如my.cnf或my.ini):

wait_timeout = 1200
interactive_timeout = 1200

注意别设得太长,避免大量空闲连接占用MySQL资源。

4. 禁用持久化连接

如果你线程里用了PDO的持久化连接(比如PDO::ATTR_PERSISTENT => true),在多线程环境下很容易出现连接泄漏或失效的问题,线程中一定要用非持久化的普通连接。

额外排查小技巧

  • 查看MySQL的错误日志,确认是不是因为空闲超时关闭了连接(日志里会有类似Aborted connection ... due to timeout的记录)。
  • 检查线程中创建连接的代码有没有捕获异常,避免默默创建了无效连接却没发现。

内容的提问来源于stack exchange,提问作者Jerem Lachkar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 13:07:51