管理システムが遅い原因は、サーバーの能力不足であることはまれです。急いで書かれた数本の問い合わせ、必要以上のデータを読む数画面、そして手を入れる前に測っていないこと。これが主な理由です。本稿ではデータに最も近い層から外側へ、着手すべき順に見ていきます。
手順0 — 直す前に測る
勘に頼った最適化は、時間を失う最短経路です。コードに触れる前に三つの数字が必要です。どの要求が最も遅いか、その中のどこが遅いか(データ取得か、処理か、転送か)、そしてその頻度です。
最低限の道具立ては、操作ごとの所要時間の記録、データベース側での高負荷な問い合わせの集計、ブラウザでの表示時間の確認です。3秒かかっても一日一回の操作より、300ミリ秒でも一日数千回動く操作のほうが重要です。
1. 問い合わせ — 時間の大半を占める場所
実務では次の四つが大半を占めます。
- 繰り返しの中の問い合わせ。一覧を取得し、各行ごとにもう一度問い合わせる。50行の画面がデータベースへの51往復になります。対処は、関連データを同じ呼び出しで取るか、まとめて先読みすることです。
- 必要以上の列を取る。画面に出るのは6列なのに、長い備考欄まで含むエンティティ全体を読んでいる。対処は、表示用オブジェクトへ直接射影することです。
- 絞り込みと並べ替えに使う列の索引不足。特に日付列と、常に絞り込みに使う外部キーです。
- 取得後に絞り込んでいる。条件をアプリ側で書くと、データベースは結局テーブル全体を読みます。
安価で効く工夫がひとつ。参照専用の画面では変更追跡を切ること。大きな一覧では、節約できるメモリと時間が無視できません。
2. ページングと絞り込み
一覧画面が全件を返してよい場面はありません。原則は次のとおりです。
- 画面が指定しない場合でも、サーバー側で最大件数を必ず強制する。
- 並び順は安定させる。第二キーを足さないと、ページ間で行が入れ替わります。
- 総件数の算出は別の問い合わせであり、本体の取得より重いことがよくあります。巨大なテーブルでは、正確な総数ではなく「次のページがある」の表示で足りないか検討してください。
3. キャッシュは置き場所を選ぶ
キャッシュは強力ですが、分かりにくい不具合を生みやすい道具です。データの性質で分けます。
- ほとんど変わらないマスタ(単位、役職、契約種別):アプリのメモリに置き、マスタ編集時に破棄します。最も報われる使い方です。
- 複数プロセスで共有するデータ:外部のキャッシュに短い有効期限で置きます。
- 取引データ:原則としてキャッシュしません。遅い数字より、誤った数字のほうが悪いからです。
原則:「何がこれを無効にするのか」に答えられるようになってからキャッシュする。答えられないならキャッシュしない。
4. 重い処理は裏方へ
大量帳票の出力、一斉通知、外部システムとの同期。いずれも利用者を待たせながら実行するものではありません。待ち行列に入れ、完了したら結果を返し、途中経過を見せます。画面が速くなるだけでなく、多人数が同時に押したときに重い処理がシステム全体を巻き込む事態も防げます。
5. 転送と画面側
- 応答を圧縮する。JSONも静的ファイルもよく縮み、費用はほぼゼロです。
- 内容に応じた名前を持つ静的ファイルは長期キャッシュにし、入口のページはキャッシュしない。新しい配信が即座に反映されます。
- 画像:アップロード時に圧縮し寸法を制限します。原寸をブラウザに落として縮小させるのは無駄です。
- 必要なときに読み込む。画面側のコードを機能ごとに分割し、初回訪問で全体を落とさないようにします。
6. 得た速度を保つ
一度最適化して放置すれば、一年で出発点に戻ります。続けるべきことは次のとおりです。
- あらかじめ決めた時間を超えた操作を自動で警告する。
- 見本データではなく実データ量で試験する。一万行のテーブルと一千万行のテーブルは、まったく違う振る舞いをします。
- 高負荷な問い合わせの一覧を定期的に見直す。業務が変われば、この一覧も変わります。
多くの管理システムは、ハードウェアに一円もかけずに数倍速くできます。条件は三つだけです。先に測ること、正しい場所を直すこと、そして寿命を管理できないものをキャッシュしないことです。




