🐈

Laravelでcsvを作成するときにAllowed memory size of ~ bytes exhausted エラーが出る時の対処

2023/11/27に公開

背景

CSVをスケジューラで作成しておく処理をLaravelで作っていたが、データ量が多くなるにつれ、Allowed memory size of 536870912 bytes exhaustedというエラーが出るようになってしまった。

原因

大量のデータを変数に格納していたこと。

対処法

元々、下記のコードで取得したデータをforeachで回し、1行ずつ書き込んでいた。

$filepath = storage_path('app') . '/filename.csv';
$stream = fopen($filepath, 'w');
$collection = Model::get();
foreach ($collection as $model) {
  fputcsv($stream, [
    'hoge' => $model->fuga,
    'hoge' => $model->fuga,
    #
    #
    #
  ]);
}
fclose($stream);

下記コードに改修

$filepath = storage_path('app') . '/filename.csv';
$stream = fopen($filepath, 'w');
Model::chunk(200, function ($collection) use ($stream) {
  foreach ($collection as $model) {
    fputcsv($stream, [
      'hoge' => $model->fuga,
      'hoge' => $model->fuga,
      #
      #
      #
    ]);
  }
}
fclose($stream);

これで1/4程度に抑えることができました。

おまけ

メモリの調査する際に下記コードがおすすめ

$filepath = storage_path('app') . '/filename.csv';
$stream = fopen($filepath, 'w');
Model::chunk(200, function ($collection) use ($stream) {
  foreach ($collection as $counter => $model) {
    fputcsv($stream, [
      'hoge' => $model->fuga,
      'hoge' => $model->fuga,
      #
      #
      #
    ]);
    if ($counter % 1000 === 0) {
      print " {$counter}ループ[メモリ使用量]:" . memory_get_usage() / 1024 . "KB\n";
       print " {$counter}ループ[メモリ最大使用量]:" . memory_get_peak_usage() / 1024 . "KB\n";
    }
  }
}
fclose($stream);

print部分はLogに直しても良いと思う。適宜。

Discussion