ついに重いという現象がこの前発生した。
あるデータを5万件ほど入れたのだ。
セレクトボックスで選択できるようにしていた。
今までは10種類程だったが、それを5万にしたのだ。
もちろん検索も可能だ。
選択するとブラウザの応答が止まる。
おそらくまだロードしているのであろう。
chromeは「応答しませんが待ちますか?」
と返答してくる。
今回の対応は全部検索にして、ワードに含むデータを取得することで解決した。
他の方法もないか探してみた。
1. ページネーションを導入する
大量のデータを一度に取得しないで、ページネーションを使い小分けにデータを取得する方法。
Laravel では paginate() や simplePaginate() を使うと簡単。
$users = User::paginate(50);
return view('users.index', compact('users'));
2. インクリメンタルサーチやオートコンプリートを導入する
大量のデータをselectタグの中にすべて表示するとブラウザがフリーズする。
対応として検索キーワード入力ごとに必要なデータのみ取得する仕組み。
JavaScript のイベントを利用して Ajax 通信をして、サーバーから対象データを返却。
プルダウンに表示する方法。
3. キャッシュを利用する
一度取得した結果をキャッシュに保存することで再取得の時間を短縮する。
Laravel にはキャッシュ機構がビルトインで存在する。
$users = Cache::remember('user:list', 3600, function () {
return User::all();
});
注意点
データが頻繁に更新される場合はキャッシュの有効期限管理が必要になる。
4. インデックスを貼る
DB のカラムにインデックスを貼ることで検索パフォーマンスを向上させる手法。
CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
検索を
SELECT *
FROM users
WHERE email = 'example@example.com';
インデックスを貼る。
CREATE INDEX idx_users_email ON users (email);
対象カラムの種類や検索条件に応じてMySQLやPostgreSQLなどのインデックス特性を理解して正しく設計するとクエリ速度が上がる。
大きなテーブルで実行速度を大幅に改善できるらしい。
ただ、データをINSERT、UPDATE、DELETEするたびにインデックスの再構築が必要になるとのこと。
5. 適切なクエリ・N+1問題の回避
with() や load() などを用いることで関連テーブルを一括取得してクエリの発行回数を減らすことができる。
// N+1問題の例
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name; // ここで都度DBアクセスが走る可能性あり
}
// withを使って回避
$posts = Post::with('user')->get();
6. Lazy Loading / チャンク処理
Eloquent には**chunk()やcursor()**メソッドが用意されている。
大量のレコードを分割して取得することが可能。
メモリを圧迫せずに処理したい場合などに有効。
// 100件ずつ取得する例
User::chunk(100, function ($users) {
foreach ($users as $user) {
// ...
}
});
バッチ処理に向いているらしい。
7. フロントエンドの非同期読み込み(Ajax / タブ区切りなどのUI)
タブを設けて必要なタイミングでデータ取得する。
ページにアクセスした際に Ajax でバックグラウンドで読み込み、完了したら表示する。
データが重くなるのがどういうことかローカル環境だとデータ量がすくなk分かりづらかった。
本番の環境に寄せることで、実際に問題が起こることを知った3月末日。