探讨一下大数据量的导出Excel 方案
导出的要求:
- 数据量可能最大10-20万不止
- 主表数据字段也很多
- 不能限制导出的日期和导出数量限制
- 低内存,不能把内存搞崩
目前的方案:
采用 分批次 ,一个批次5000 条, 导出成excel, 最后再把 这一批次的excel 压缩成 zip 格式提供给客户下载, 完成整体需求 扩展用的是fast-excel
压缩扩展用的是自己二次封装的:flttgo/easy-zipper
因为原作者不维护了,但是我们PHP 版本比较低,所以进行了二次处理
有好的方案欢迎留言讨论,目前不知道还有啥方案,其他语言的方案暂且不考虑,优先PHP ,其他语言对于这个问题,有同样的问题和难点,无论怎么异步,内存这块避免不掉
我的方案部分核心代码
生成批次队列的代码
$jobs = [];
$chunks = ApplyRisk::getListQuery(ApplyRiskFilter::apply($params, 'afterLoan'))
->select(['apply_risk_id'])
->chunkById(5000, function ($rows, $page) use (&$jobs) {
$ids = $rows->pluck('apply_risk_id')->toArray();
$jobs[] = new AfterLoanExportBatchJob($this->task, LazyCollection::make($ids), $page);
});
unset($chunks);
$taskId = $this->task->id;
Bus::batch($jobs)
// 所有任务完成才执行的回调
->finally(function (Batch $batch) use ($taskId) {
if ($batch->finished()) {
dispatch(new AfterLoanExportFinishJob($taskId));
}
})->dispatch();
unset($jobs);
批次队列导出格式的数据采用 yield
生成, 字段用filed 替代
public function genertateLazyData($chunks)
{
/** @var \Illuminate\Support\LazyCollection $chunk */
yield from ApplyRisk::select([
'field'
])->whereIn('apply_risk_id', $chunks)->lazy()->each(function ($row) {
yield $this->transform->afterLoan($row);
});
}
导出excel 代码
$dir = storage_path($this->task->hash_id);
if (!File::exists($dir)) {
if (! @mkdir($dir, 0755, true) && ! is_dir($dir)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $dir));
}
}
$fileName = $dir.'/'.$this->task->title.'-'.$this->index.'.xlsx';
//数据准备完毕,开始导出
FastExcel::data($this->genertateLazyData($this->riskIds))->export($fileName);
上面是我这次处理的核心逻辑
推荐文章: