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

PHP脚本执行后内存未释放问题求助(LAMP环境)

解决PHP查询百万级数据内存占用过高且无法释放的问题

核心问题分析

你当前用fetchAll(PDO::FETCH_ASSOC)一次性将100万行数据加载到PHP内存中,这本身就会占用大量内存;加上Apache进程模式下,PHP内存是进程级的,即使调用unset或gc_collect_cycles,内存也会被PHP内存池保留(用于后续请求复用),不会立即归还给系统,多次请求后内存累积导致进程崩溃。

有效解决方案

1. 逐行读取数据,避免一次性加载全量结果

放弃fetchAll,改用循环逐行获取数据,每次仅在内存中保留一行数据,处理完成后自动释放,内存占用会大幅降低:

$db = new PDO("mysql:host=$dbhost;dbname=$dbname;", $dbuser, $dbpass); 
$req = $db->prepare('SELECT open, high, low, close, volume, timestamp FROM '.$f_dataset); // 只查需要的字段
$req->execute();

// 逐行处理数据
while ($row = $req->fetch(PDO::FETCH_ASSOC)) {
    // 在这里执行单条蜡烛数据的处理逻辑(比如计算、写入文件等)
}

$req->closeCursor();
unset($req, $db);

2. 优化查询语句,只获取必要字段

不要用SELECT *,明确指定策略测试需要的字段(比如时间、开高低收、成交量),减少单条数据的内存占用,进一步降低整体内存消耗。

3. 调整Apache进程配置,强制释放内存

针对Apache的prefork模式,修改配置文件(httpd.conf或apache2.conf),设置子进程的请求上限和内存限制,让进程在达到阈值后自动重启,释放内存:

<IfModule mpm_prefork_module>
    MaxRequestPerChild 100  # 每个子进程处理100个请求后自动重启
    RLimitMEM 768M          # 限制单个进程最大内存占用,超过则被系统终止
</IfModule>

4. 改用PHP CLI模式运行脚本

如果交易策略测试工具不需要通过Web访问,直接用CLI模式执行脚本。脚本执行完成后进程会直接退出,系统会完全回收所有内存,不会出现内存累积问题:

php your_strategy_test_script.php

5. 启用PDO非缓冲查询

通过设置MYSQL_ATTR_USE_BUFFERED_QUERY => false,让PDO不将全量结果加载到PHP内存,而是直接从MySQL服务器逐行读取。注意:非缓冲查询下必须处理完所有结果才能执行下一个数据库操作,且无法使用rowCount()获取总行数:

$db = new PDO("mysql:host=$dbhost;dbname=$dbname;", $dbuser, $dbpass, [
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false
]); 
$req = $db->prepare('SELECT open, high, low, close, volume, timestamp FROM '.$f_dataset);
$req->execute();

while ($row = $req->fetch(PDO::FETCH_ASSOC)) {
    // 处理数据
}

$req->closeCursor();
unset($req, $db);

为什么unset和gc_collect_cycles无效?

在Apache的进程模式下,PHP的内存由进程管理,unset只是标记变量为垃圾,gc_collect_cycles会回收PHP内存池中的垃圾,但这些内存不会立即归还给操作系统,而是被PHP保留用于后续请求。只有当Apache子进程结束时,操作系统才会回收该进程占用的所有内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 19:10:18