Laravelで本番DBを使用したらallowed memory size exhausted


状況

laravelで作ったプロジェクトを本番DBを接続したら、以下のエラーが発生。

コマンドライン
Allowed memory size of 134217728 bytes exhausted (tried to allocate xxxxxxxx bytes)

試験環境のDBテーブルではレコードがせいぜい数百件しかなかったので何も問題が起きなかったが、本番DBテーブルでは数十万件のレコードがあった。
この状態で多対多リレーションを書き換えようとしたことが原因。

解決

はじめはphpのプロセスメモリ使用制限を設定しているシステム変数memory_limitで設定を変更した。

php.ini
memory_limit=2M
systemctl restart

しかし、これでもエラーが出続けた。

調べるとlaravelのEloquentにchunk(), chunkByID()、cursor()があり、今回はデータの更新だったのでchunkByID()を利用。
多対多リレーションで使用したテーブルはlettersとstampsだったので、中間DBテーブルをletter_stampで作成。

LetterController.php
$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()