关于Laravel 队列内存的一个占用大小的设置[问题已测试解决]

大数据导出扩展推荐:
评论里推荐的这个也不错 最原始的
xlswriter
我把这几个测试截图都放到一起,大家参考使用
fast-excel-writer

关于Laravel  队列内存的一个占用大小的设置[问题已测试解决]

fast-excel

关于Laravel  队列内存的一个占用大小的设置[问题已测试解决]

这个需要安装扩展,也很快
php-ext-xlswriter

关于Laravel  队列内存的一个占用大小的设置[问题已测试解决]

最近在测试 laravel queue database 驱动时候,使用 queue:work 频繁退出,发现是队列执行的内存 memory = 128M 超了, 实际队列内部执行下来是 144M ,所以造成了队列的频繁退出

队列内存大的起因: 系统导出大量的数据,结果是 Excel ,采用异步 + queue 的方式进行到处,每个 Excel 限制是 5000 条数据,里面涉及到 ORM 模型的查询,采用的方式都是 ID chunkById 方式,字段也限制了

问题:想问问大家的 excel 在处理这种单个队列内存过大,有设置过内存大小没,具体这个东西怎么来评判合适

我的目前方案:减少项目的 ORM 调用, 尽量使用 lazycollection 缩小内存:

伪代码:

$chunks = ApplyRisk::getListQuery(ApplyRiskFilter::apply($params, 'beforeLoan'))
            ->select(['apply_risk_id'])
            ->lazyById(2000)
            ->chunk(5000);

        $this->task->update([
            'status' => TaskStatusEnum::ready()
        ]);


        $jobs = [];

        foreach ($chunks as $key => $chunk) {
            $ids = $chunk->pluck('apply_risk_id')->toArray();
            $jobs[] = new BeforeLoadExportBatchJob($this->task, LazyCollection::make($ids), $key + 1);
        }

        unset($chunks);


        $batch = Bus::batch($jobs)
            // 所有任务完成才执行的回调
            ->finally(function (Batch $batch) {

                //if ($batch->finished()) {
                //    $this->task->update([
                //        'status' => TaskStatusEnum::finished()
                //    ]);
                //}

            })->dispatch();

使用了 Larvle 内置的批处理队列代码:Bus::batch 现在我遇到的问题是每个 batch job 需要处理的数据量也就 5000 条,但是还是会造成队列 quit, 指定参数:queue:work --memory=256 问题虽然能解决,但并不是我最想要的,我是想问下在大量数据下,分块情况下如果更大程度的节省内存


解决方案来了#

查了资料,解决方案如下

解决方案来了:
目前单次导出大概数据量在 2-5 万附近 队列采用 database 驱动 导出的扩展用的是 fast-excel
主队列

$jobs = [];
        $chunks = ApplyRisk::getListQuery(ApplyRiskFilter::apply($params, 'beforeLoan'))
            ->select(['apply_risk_id'])
            ->chunkById(5000, function ($rows, $page) use (&$jobs) {
                $ids = $rows->pluck('apply_risk_id')->toArray();
                $jobs[] = new BeforeLoadExportBatchJob($this->task, LazyCollection::make($ids), $page + 1);
            });


        unset($chunks);

        Bus::batch($jobs)
            // 所有任务完成才执行的回调
            ->finally(function (Batch $batch) {

                //if ($batch->finished()) {
                //    $this->task->update([
                //        'status' => TaskStatusEnum::finished()
                //    ]);
                //}

            })->dispatch();

        unset($jobs);

批次队列导出 Excel:
节省核心内存在这,采用 yield 生成器的方式传入

 //数据准备完毕,开始导出 
        FastExcel::data($this->genertateLazyData($this->riskIds))->export($fileName);

public function genertateLazyData($chunks)
    {
        /** @var \Illuminate\Support\LazyCollection $chunk */
       //  省略了 select 此处
        yield from ApplyRisk::whereIn('apply_risk_id', $chunks)->lazy()->each(function ($row) {
            yield $this->transform->beforeLoan($row);
        });
    }

内存占用情况:

file

使用内存记录方式:

// 单位是M
Log::info('Batch队列内存记录:'. memory_get_usage(true) / 1024/ 1024);
每天一点小知识,到那都是大佬,哈哈
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
最佳答案

解决方案参考上文,如果有需要的朋友可参考,目前单次导出数据量在 2-5 万, 采取的方案是批量导出一次 5000 条 最终在压缩到一起提供下载,使用的扩展是 fast-excel ,最终导出数据采用的是 yield 生成器方式,laravel queue 默认 memory = 128M 直接导出 一直在 144M 附近,导出一次队列就停止了, 这种方式可以限定单个队列的内存稳定在 50M 附近,导出完毕,释放变量

1年前 评论
讨论数量: 22

其实大部分问题可能不是 orm 的问题,而是 excel 占据的内存,可以尝试一下分批写入 xlswriter-docs.viest.me/

1年前 评论
raybon (楼主) 1年前
raybon (楼主) 1年前

可以考虑导出 csv 文件, 也可以用 excel 打开, csv 是文本格式的。

文本格式可以考虑只写, 应该可以避免超内存

1年前 评论
raybon (楼主) 1年前
mowangjuanzi 1年前
kis龍 (作者) 1年前
sanders 1年前
raybon (楼主) 1年前
kis龍 (作者) 1年前
raybon (楼主) 1年前

大部分的 Excel 扩展包都是在内存中处理读写结果,文件内容越大占用内存越高。可以考虑下这个 PHP 扩展,支持固定内存的读取和导出且性能很高,缺点就是需要编译安装且仅支持 xlsx 格式:github.com/viest/php-ext-xlswriter

1年前 评论
raybon (楼主) 1年前
Molin (作者) 1年前
raybon (楼主) 1年前

go 的性能很好

1年前 评论
raybon (楼主) 1年前

解决方案参考上文,如果有需要的朋友可参考,目前单次导出数据量在 2-5 万, 采取的方案是批量导出一次 5000 条 最终在压缩到一起提供下载,使用的扩展是 fast-excel ,最终导出数据采用的是 yield 生成器方式,laravel queue 默认 memory = 128M 直接导出 一直在 144M 附近,导出一次队列就停止了, 这种方式可以限定单个队列的内存稳定在 50M 附近,导出完毕,释放变量

1年前 评论
1年前 评论
raybon (楼主) 1年前
徵羽宫 (作者) 1年前
raybon (楼主) 1年前