关于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);
每天一点小知识,到那都是大佬,哈哈
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
最佳答案

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

8个月前 评论
讨论数量: 22

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

8个月前 评论
raybon (楼主) 8个月前
raybon (楼主) 8个月前

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

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

8个月前 评论
raybon (楼主) 8个月前
mowangjuanzi 8个月前
kis龍 (作者) 8个月前
sanders 8个月前
raybon (楼主) 8个月前
kis龍 (作者) 8个月前
raybon (楼主) 8个月前

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

8个月前 评论
raybon (楼主) 8个月前
Molin (作者) 8个月前
raybon (楼主) 8个月前

go的性能很好

8个月前 评论
raybon (楼主) 8个月前

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

8个月前 评论
8个月前 评论
raybon (楼主) 8个月前
徵羽宫 (作者) 8个月前
raybon (楼主) 8个月前

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