数万件、数十万件のデータを処理する場合、単純な繰り返し処理で1件毎にデータを処理すると、終了までに数時間以上かかってしまうことも珍しくありません。
こういった処理は少し方法を変えるだけで10倍以上の高速化ができる場合があるので、その方法を処理内容別に記載していきます。
データの作成(Create)
insertを使う
insert()
メソッドを使うことで、大量のレコードを一括で高速に挿入することができます。
$data = [];
for ($i = 0; $i < 1000; $i++) {
$data[] = ['name' => 'user_name'.$i, 'email' => 'user'.$i.'@example.com'];
}
DB::table('users')->insert($data);
※1000件程度であれば上記コードのように一括でinsertを行っても問題無いケースが多いですが、10万件等の大量のデータの場合はメモリ不足等が発生する可能性が有ります。その場合は以下のように分割してinsertを行います。
$data = [];
for ($i = 0; $i < 100000; $i++) {
$data[] = ['name' => 'user_name' . $i, 'email' => 'user' . $i . '@example.com'];
}
// 1000件ずつに分割してinsert
foreach (array_chunk($data, 1000) as $chunk) {
DB::table('users')->insert($chunk);
}
※上記のように一括作成を行った場合、Observerでモデルのイベント(created等)を設定していても発火しません。イベントの実行が必要な場合はcreateメソッド等で1件ずつ作成する必要が有ります。
読み取り(Read)
chunkを使う
上述の「データの作成」にも書きましたが、大量のデータを一度にメモリに読み込むとメモリ不足が発生して処理が遅くなることがあります。その場合はchunk()
メソッドを使ってデータを分割して処理することで高速化できます。
User::where('status', 'active')->chunk(1000, function ($users) {
foreach ($users as $user) {
// ユーザーごとに処理
echo $user->name;
}
});
※chunkメソッドは大量データを分割して取り出すのに便利ですが、処理中にテーブルのデータが変化する可能性がある場合、同じデータが複数回読み込まれる/一部のデータが読み込まれない といった問題が起こり得ます。
そのような状況を避けたい場合は、主キーの順序を考慮してchunkメソッドの代わりにchunkByIdメソッドを使う、あるいは処理前後でテーブル更新を止めておくなどの対応が必要です。
更新(Update)
モデル取得前に一括更新
モデルを取得してから更新→保存するのではなく、取得前の段階でupdate()
メソッドを使えば、特定の条件に合致するレコードを一括で高速に更新することができます。
User::where('status', 'active')->update(['status' => 'inactive']);
※上記のように一括更新を行った場合、Observerでモデルのイベント(updated等)を設定していても発火しません。イベントの実行が必要な場合はモデルの取得後に更新処理を行う必要があります。
削除(Delete)
モデル取得前に一括削除
上述の「更新」と同様に、モデル取得前の段階でdelete()
メソッドを使えば一括で高速に削除することができます。
User::where('created_at', '<', now()->subYear())->delete();
※こちらも同様で、上記のように一括削除を行った場合はObserverのイベント(deleted等)が発火しません。イベントの実行が必要な場合はモデル取得後に削除処理を行う必要があります。
本記事は以上となります。何かおかしな部分や間違った記述などありましたら教えて頂けると嬉しいです!