🐘
Laravelでの大量データの挿入を高速化する方法(バルクインサート)
業務中の出来事
既存システムを確認している中で、保存するデータ数がそれほど多くないにもかかわらず、処理時間が想定以上に長くなっている箇所がありました。
コードを確認してみると、1行ずつinsert()メソッドを使用してデータを登録していることが原因でした。
今回は、その問題点と改善案を以下に記載します。
【改善前】1行ずつinsert()する場合の問題点
1行ずつ挿入する処理の場合
foreach ($data as $row) {
DB::table('users')->insert([
'name' => $row['name'],
'email' => $row['email'],
'password' => bcrypt($row['password']),
]);
}
この方法では、1つのレコードを挿入するたびにデータベースへのクエリが発行されます。
その結果、数百行、数千行とデータが増えるにつれて、データベースへの接続やクエリのオーバーヘッドが大きくなり、パフォーマンスが低下します。
【改善後】バルクインサートを使った高速化
1行ずつ挿入する代わりに、一定の件数ごとにデータをまとめて挿入することで、クエリの回数を減らし、データベース操作の効率を大幅に向上させることができます。
Laravelでは、配列を使用して複数行を一度にinsert()することが可能です。
1000行ずつまとめてデータを挿入する場合
(分割数はデータや実行時間に合わせて任意の数を使用)
$batchSize = 1000; // 一度に挿入する行数
$chunks = array_chunk($data, $batchSize); // データを1000行ごとに分割
foreach ($chunks as $chunk) {
DB::table('users')->insert($chunk); // 1000行ずつまとめて挿入
}
このコードでは、array_chunk()関数を使用して、データを1000行ごとの配列に分割し、insert()メソッドでまとめて挿入しています。
この方法により、1回のクエリで大量のデータを挿入できるため、クエリの発行回数が大幅に減り、パフォーマンスが向上します。
バルクインサートの利点
-
クエリ回数の削減:
各行で個別にクエリを発行するより、一度にまとめて挿入することでデータベースへの接続負荷が減少し、全体の処理速度が向上します。 -
パフォーマンスの向上:
クエリの発行頻度を減らすことでサーバーリソースの消費を軽減し、システム全体のパフォーマンスを改善できます。 -
メモリの節約:
データを分割して挿入するため、一度に大量のメモリを消費することがありません。特に大規模なデータセットを扱う場合、メモリ使用量の削減が重要です。
まとめ・感想
- 1行ずつ
insert()するのは、データ量が少ない場合は問題ありませんが、大量データでは効率が悪くなります。 - バルクインサートを使用して、1000行ごとにデータをまとめて挿入することで、クエリ回数を減らし、処理速度を大幅に向上させることができます。
- 今後リファクタリングするうえで、確認しておきたいポイントの一つと感じたため、メモとして記事に残しました。
Discussion