取出大量数据后,为什么内存会不断累加?没有释放呢?

今天遇到个问题关于内存在服务器中不断增长。
目前需求是从数据库中取出大量数据,大概10W-20W条左右,按照格式写入到文件中。
先不考虑写入文件,每取出一次数据,服务器的内存占用就会累加。由于数据量大,我再php.ini中配置了memory_limit=2000M实际上我觉得用不了这么多,取出10W条数据也就几十M足以,但程序每取出一次数据内存占用就会被保持。请求两三次服务器的2G内存就被用光了,程序无法正常结束。

使用的这样的查询方式

$data = DB::table($tbl_name)->select($cols)->get();

希望各位大侠帮忙
在这提前谢过了

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 19

详细点,不然

5年前 评论

@xuanjiang1985
每次查询完内存没被释放的感觉

file

我之后试了一下别的查询语句也是如此,每查一次内存被占用的越来越大,内存用光只是时间的问题

file

这是多次请求后的结果,而且并没有取出大量数据的情况下,内存占用越来越多,重启服务后恢复

5年前 评论

会不会有可能是这个原因:

为了避免每次变量的refcount减少的时候都调用GC的算法进行垃圾判断,此算法会先把所有前面准则3情况下的zval节点放入一个节点(root)缓冲区(root buffer),并且将这些zval节点标记成紫色,同时算法必须确保每一个zval节点在缓冲区中之出现一次。当缓冲区被节点塞满的时候,GC才开始开始对缓冲区中的zval节点进行垃圾判断。

5年前 评论

@xuanjiang1985 那如果要是这样的话,我的几十M的数据,为什么取出来会占用好几百M的内存呢?而且我把取出来的资源都unset掉也没用

5年前 评论
leo

是不是用了类似 swoole 的类库?

5年前 评论

@leo 没有,用的是laravel5.5,composer安装了aws的sdk。之后发现取少量数据,内存也会不断上涨,不知道为什么,每次涨完就不下来了

5年前 评论

现在服务器内存是2G,php.ini配置的memory_limit=1500M,第一次运行程序内存先升到1.2G,再降到700M,第二次运行升到1.4G再降到1.1G,第三次就没有响应了,返回状态是500

5年前 评论
DianWang

线上用xhprof做统计分析,本地用xdebug做测试,观察内存的变化对检测错误没有帮助。

5年前 评论

@Breezy 改这个 memory_limit 是最不推荐的,显得太粗暴和业余了。自己要学会分析是什么原因造成内存泄漏,特别是在循环体里面,对占用内存多的变量(特别是对象)要格外留意。可以尝试及时 unset 已经没有用的变量。下面提供一个利用 yield 的方案:

$db =  DB::table($tableName)->select($cols);

$func = function ($count)  use ($db) {
    for ($i = 0; $i < $count; $i += 1000) {
         yield $db->offset($i)->limit(1000)->get();
    }
};

$count = 50000; // 你想导出的数量

foreach ($func($count) as $data) {
    // ...
}

不要再用 $tbl_name 这种变量名了!

5年前 评论

@Littlesqx 感谢,其实主要还是取数据那一下,太大量内存耗费非常大,之后用laravel带的chunk取数据再做操作就没问题了。还有为啥不能用$tbl_name这种变量名啊。。

5年前 评论

也可以吧,就是建议统一风格

5年前 评论

@Littlesqx 好的,感谢。确实修改php配置的内存太业余了

5年前 评论
lmaster

@Breezy 是否和数据库有关系,你的业务逻辑要每次都取出这十几万条数据么?可否一部分一部分取出呢?

5年前 评论

@lmaster 跟数据库没太大关系,就是把这么大的数据量按格式生成txt,主要时间花费还是在数据库的查询,基本每次要十几秒,不过写文件的速度还是很快的。最终选择是用chunk取数据,效果非常好

5年前 评论
lmaster

@Breezy 我的意思是 你在php.ini 中设置了最大运行内存(如果生效的话),那么 php 所占用的内存就是一定的了啊,如果只有你在访问(而且是单个请求)那么 php 的内存怎么会超出那?除非有类似池之类的东西,php 是执行完就销毁的,但是数据库也有内存占用(比如innodb缓存之类),或者如果是 linux 那么系统也会占内存(linux 内存与 win 使用方式不一样,linux 高效的原因有一部分就是内存利用高),也就是说,你为什么认定是 php 的内存使用高,或者说是内存“泄漏”。php 的内存管理很厉害的基本不会出现内存泄漏的

5年前 评论

@lmaster 哦哦,这种可能还真没想到。首先php和mysql不在一台服务器上,当时是每次查询完内存会剧增,基本山查三次程序就运行不起来了,只能重启服务。如果有池的话确实不清楚是什么样的规则,和怎么设置的。确实很奇怪的一点是我设置了最大运行内存是1G,但是拿数据还是往1个多G跑甚至2G。

5年前 评论
lmaster

@Breezy 那你看看 php 缓冲是不是有关系那?output_buffering 类似之类的,memory_get_usage()(获取当前脚本内存占用单位 B 你在脚本开始输出一下,在结束时在输出一下,来确定一下)

5年前 评论

@lmaster 之前也试过了,也没占很大内存,不过这个output_buffering一直没动过,4096。

5年前 评论

我现在把 chunk 改用游标,不会增大了。黑人问号脸。

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!