是的,mysqli_close() 后连接不一定断开;持久连接下仅归还连接池,普通连接才真正关闭。需区分连接类型,及时释放结果集与语句对象,cli 脚本须用 try/finally 或 register_shutdown_function 兜底。

PHP mysqli_close() 之后连接真的断了吗?
不一定。如果用的是持久连接(mysqli_pconnect() 或 PDO 配置了 PDO::ATTR_PERSISTENT => true),调用 mysqli_close() 实际上只是把连接归还给连接池,不会真正断开。这时候你看到的“已关闭”是假象。
实操建议:
- 确认是否用了持久连接:查代码里有没有
mysqli_pconnect(),或new PDO(...)时传了[PDO::ATTR_PERSISTENT => true] - 普通连接(
mysqli_connect())可以安全调用mysqli_close($conn),但不是必须——脚本结束时 PHP 自动释放 - 如果用
PDO,$pdo = null比$pdo->close()更可靠(PDO本身没有close()方法)
mysql_free_result() 和 unset() 哪个该先做?
先 mysql_free_result()(对应 mysqli_free_result()),再 unset()。否则结果集内存可能卡在 PHP 引用计数里不释放,尤其处理大查询时容易 OOM。
常见错误现象:mysqli_query() 返回 mysqli_result 对象后,只 unset($result) 却没调用 mysqli_free_result($result),导致内存持续增长
立即学习“PHP免费学习笔记(深入)”;
使用场景:批量读取百万级数据、导出报表、CLI 脚本长运行
实操建议:
-
mysqli_free_result($result)必须在遍历完$result后立刻执行 - 之后可选
unset($result),但非必需;重点是前者 - 如果用
fetch_all(MYSQLI_ASSOC)一次性取回数组,就不用free_result()—— 结果集已转为 PHP 数组,原资源自动释放
PDO 中 fetch() 之后要不要手动释放?
不用。PDO 的 fetch()、fetchAll() 等方法不持有底层结果集句柄,它们只是从已缓存的结果中读数据。真正要管的是 PDOStatement 对象本身。
性能影响:如果反复执行相同 SQL 但不复用 PDOStatement,每次 prepare() + execute() 都会新建对象,小概率触发句柄泄漏
实操建议:
- 执行完查询后,显式置空语句对象:
$stmt = null(比unset($stmt)更明确) - 避免在循环里反复
$pdo->prepare($sql),应提前 prepare,循环中只execute() - 如果启用了
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY(默认开启),结果集已缓冲,无需额外释放;关掉它(流式查询)时,务必确保遍历完成,否则后续查询会报Cannot execute queries while other unbuffered queries are active
CLI 脚本里忘了关连接,会撑爆 MySQL 吗?
会。MySQL 的 max_connections 是全局限制,PHP-FPM 进程退出时连接才释放,但 CLI 脚本若异常终止(如 die()、信号中断、未捕获异常),连接可能滞留到 MySQL 的 wait_timeout(默认 8 小时)才被服务器端断开。
容易踩的坑:
- 脚本里用
pcntl_fork()后,子进程继承父进程的数据库连接,但父子都以为自己该负责关闭 → 双重 close 可能报错,漏关又导致连接堆积 - try/catch 里没写
finally块释放资源,异常跳出后连接悬空 - 连接成功后中间有耗时操作(如 curl、sleep),期间 MySQL 主动断连,但 PHP 连接对象仍处于“打开”状态,下次 query 才报错,此时连接其实早废了
最稳妥的做法:所有数据库操作包在 try/finally 里,finally 中确保 mysqli_close() 或 $pdo = null 执行;CLI 脚本开头可设 register_shutdown_function() 做兜底清理











