子进程继承父进程数据库连接导致“mysql server has gone away”,须在pcntl_fork()后子进程中立即释放旧连接并重建新连接;父进程不得使用持久化连接,且需正确处理sigchld防止僵尸进程影响连接稳定性。

子进程继承父进程数据库连接句柄导致断连
pcntl_fork() 会完整复制父进程的内存空间和所有打开的资源句柄,包括 PDO 或 MySQLi 的连接。但底层 socket 文件描述符是共享的——父子进程共用同一个 TCP 连接。一旦任一进程关闭连接(比如父进程结束、异常退出或显式 close),另一方再操作就会触发 PDOException 或 MySQL server has gone away 错误。
子进程必须主动重建数据库连接
不能复用父进程的连接对象,这是最核心的规避手段。子进程进入 if ($pid === 0) 分支后,第一件事就是销毁旧连接并新建独立连接:
- 调用
$pdo = null或unset($pdo)显式释放引用(防止 GC 延迟) - 立即重新初始化数据库连接,使用全新配置(哪怕参数相同)
- 不要在 fork 前建立连接;若必须提前准备,至少把连接对象声明为
static或全局变量,并在子进程中强制重置
示例片段:
$pid = pcntl_fork();
if ($pid === -1) {
die('fork failed');
} elseif ($pid === 0) {
// 子进程:清理并重建连接
$pdo = null;
$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
// ... 执行任务
exit(0);
} else {
// 父进程继续用原连接
}
父进程连接是否要设为持久化?不推荐
启用 PDO::ATTR_PERSISTENT 不仅不能解决问题,反而加剧风险:持久连接由进程独占,子进程 fork 后拿到的是同一连接池中的“脏”句柄,更易出现状态错乱或连接被意外回收。实际线上环境应禁用持久化,让每个进程管理自己的连接生命周期。
额外注意:连接超时与信号干扰
子进程若长时间无数据库操作,可能触发服务端 idle timeout 断连;同时,父进程若未正确处理 SIGCHLD,或子进程异常退出未被 wait 回收,也可能间接影响连接稳定性。建议:
- 子进程中设置
mysqli_options($link, MYSQLI_OPT_CONNECT_TIMEOUT, 5)或 PDO 的PDO::ATTR_TIMEOUT - 父进程注册
pcntl_signal(SIGCHLD, 'sigchld_handler')并在 handler 中调用pcntl_waitpid(-1, $status, WNOHANG),避免僵尸进程堆积导致系统资源耗尽,进而影响新连接建立
真正麻烦的不是 fork 本身,而是连接这种有状态资源的隐式共享——它不会报错,直到某次 query 突然失败,且错误堆栈里完全看不到 fork 的痕迹。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











