状況
laravelで作ったプロジェクトを本番DBを接続したら、以下のエラーが発生。
Allowed memory size of 134217728 bytes exhausted (tried to allocate xxxxxxxx bytes)
試験環境のDBテーブルではレコードがせいぜい数百件しかなかったので何も問題が起きなかったが、本番DBテーブルでは数十万件のレコードがあった。
この状態で多対多リレーションを書き換えようとしたことが原因。
解決
はじめはphpのプロセスメモリ使用制限を設定しているシステム変数memory_limitで設定を変更した。
memory_limit=2M
systemctl restart
しかし、これでもエラーが出続けた。
調べるとlaravelのEloquentにchunk(), chunkByID()、cursor()があり、今回はデータの更新だったのでchunkByID()を利用。
多対多リレーションで使用したテーブルはlettersとstampsだったので、中間DBテーブルをletter_stampで作成。
$count = 0;
DB::table('letter_stamp')->where('active', true)
->chunkById(100, function ($pivots) {
foreach ($pivots as $pivot) use($count) {
$pivot->update(['stamp_id'], null);
$count++;
}
});
中間テーブルのレコードを100件ずつ取得し、値の更新。
その際にクロージャへ親スコープから$count変数を引き継いで、更新件数を把握している。
おまけ
Laravel日本語版公式サイトによるとchunk()では
クロージャから falseを返えせば、それ以上のチャンクの処理を停止できます。
とのことなので、return falseで処理から抜けることができます。
参考資料
Laravel8.x 公式
チャンキングで使用メモリを抑える
LaravelでデータをDBに保存したいときのメモリ不足をなんとかする
Laravel で chunk を使いながらトータル件数を計算
Laravel(Eloquent): chunk() vs cursor()