改善・モダナイゼーション

DBボトルネックで遅いWebサービスの改善手順

2026年07月11日
パフォーマンス データベース 既存改善 ボトルネック 高速化

「最初は速かったのに、データが増えるにつれてじわじわ重くなってきた」——サービスが順調に成長している証拠でもあるのですが、同時にこれは多くのプロダクトが一度は通る壁でもあります。ユーザーからは「ページが固まる」「保存ボタンを押しても反応が遅い」というクレームが増え、サポートは「サーバーが重いようです」としか答えられない。エンジニアに聞いても「たぶんDBが原因だと思います」という曖昧な返事しか返ってこない——こうした相談を、私たちはよく受けます。

体感として「DBが重い」ことまでは分かっていても、具体的にどのクエリが・なぜ遅いのかを特定できていないケースがほとんどです。原因を特定しないままサーバーのスペックを上げても、根本原因のクエリはそのまま遅く、数ヶ月後にまた同じ壁にぶつかります。この記事では、DB負荷による応答遅延の原因の見つけ方から、具体的な改善の打ち手、そして「スケールアップの前にやるべきこと」までを順を追って整理します。

まず疑うべきは「DBが遅いのか」の切り分け

「サービスが重い」という症状には複数の原因がありえます。アプリケーションサーバーのCPU逼迫、外部APIへの呼び出し待ち、N+1クエリによるアプリ側の処理過多、そしてDB自体の処理遅延です。改善に着手する前に、まずどこがボトルネックかを切り分ける必要があります。

  • APM(New Relic、Datadog等)やRailsであれば rack-mini-profiler で、リクエスト全体の処理時間のうちDBクエリが占める割合を確認する
  • DBサーバー自体のCPU使用率・メモリ使用率・ディスクI/Oを監視ダッシュボードで見る。DBのCPUが常時高止まりしていれば、DB側の処理そのものが重い可能性が高い
  • 接続プールの待ち時間が伸びていないか(後述の接続数枯渇のサイン)を確認する

ここを飛ばしていきなり「DBのスペックを上げよう」と判断すると、実はアプリ側のN+1クエリが原因だった、というケースで無駄なコストをかけることになります。既存プロダクト改善に着手する前の基本的な確認観点は改善の最初の確認観点にまとめていますので、あわせて参考にしてください。

ボトルネックの見つけ方

DBが原因だと確認できたら、次は「どのクエリが遅いか」を具体的に特定します。

スロークエリログを有効にする

PostgreSQLであれば log_min_duration_statement を設定し、一定時間(例: 500ms)を超えたクエリをログに出力します。MySQLでも同様に slow_query_log を有効化できます。

  • 本番環境でスロークエリログが有効化されていないプロダクトは驚くほど多く、「体感で重い」という曖昧な情報しかない状態から抜け出せていません。まずここを有効にするだけで、改善の優先順位が数値で見えるようになります
  • ログを集計し、実行回数×平均実行時間が大きいクエリから着手します。1回だけ極端に遅いクエリより、頻繁に呼ばれてじわじわ積み上がっているクエリのほうが体感への影響は大きいことが多いです

EXPLAIN(実行計画)で原因を読む

遅いクエリを特定したら、EXPLAIN ANALYZE で実行計画を確認します。

  • Seq Scan(順次スキャン)が大きなテーブルに対して発生していないか。本来インデックスを使って絞り込めるはずの検索が、テーブル全件を舐めていないか
  • 見積もり行数(rows)と実際の行数が大きく乖離していないか。乖離が大きい場合、統計情報が古く、プランナが誤った実行計画を選んでいる可能性がある(ANALYZE コマンドで統計情報を更新する)
  • JOINの結合方式(Nested Loop / Hash Join / Merge Join)が想定と違わないか。大きいテーブル同士の結合で Nested Loop が選ばれていると極端に遅くなることがある

実行計画を読み慣れていない発注者の方でも、「Seq Scanが出ているかどうか」だけ確認できれば、開発チームへの質問の解像度が大きく上がります。

典型的な原因

私たちが改善案件で実際に遭遇する原因は、パターン化するとおおむね次の4つに集約されます。

1. インデックス不足・フルスキャン

WHERE句やJOIN条件、ORDER BYに使われているカラムにインデックスが張られていないと、テーブルが小さいうちは気づかず、データが数万〜数十万件を超えたあたりから急激に遅くなります。特に「作った当初は速かったのに、最近急に重くなった」という症状はこのパターンが典型的です。

逆に、インデックスを闇雲に増やすのも問題です。書き込み(INSERT/UPDATE)のたびに全インデックスの更新コストがかかるため、使われていないインデックスは削除対象になります。

2. N+1クエリとアプリ側の設計

DBそのものは速くても、1リクエストの中で同じテーブルへのクエリが数十〜数百回発行される「N+1クエリ」があると、DB全体の負荷として重くのしかかります。Railsであれば includespreload での事前読み込み、Bulletgemのような検知ツールの導入で対処します。

3. 接続数の枯渇

アクセスが増えると、DBへの同時接続数が上限に達し、新しい接続を待つリクエストがタイムアウトする現象が起きます。

  • コネクションプール(Rails標準の ActiveRecord プール、あるいはPgBouncer等)のサイズが適切に設定されているか確認する
  • Webサーバーのワーカー数・スレッド数に対してDBの max_connections が不足していないか
  • 長時間居座る不要なトランザクション(コネクションを掴んだまま解放しない処理)がないか

4. ロック競合

同一行を複数のリクエストが同時に更新しようとすると、ロック待ちが発生し応答が遅延します。特にバッチ処理と通常のユーザーリクエストが同じテーブルを同時に触る構成では、バッチ実行中だけ全体が重くなる、という症状が出やすくなります。長時間のトランザクション内で不要な行ロックを保持していないか、バッチはユーザーの少ない時間帯にずらせないか、といった観点で見直します。

改善の打ち手

原因が特定できたら、影響が大きく・リスクが低いものから順に着手します。

インデックスの追加・見直し

最も費用対効果が高い改善です。ただし、大きなテーブルへのインデックス作成は本番トラフィックをロックすることがあるため、PostgreSQLであれば CREATE INDEX CONCURRENTLY を使い、サービスを止めずに追加します。

クエリ自体の改善

SELECT対象のカラムを絞る、不要なJOINを削る、集計処理をアプリ側からSQLに寄せる(あるいはその逆)など、クエリの書き方自体を見直します。ORMが自動生成する非効率なクエリを、必要な箇所だけ生SQLやAREL、クエリオブジェクトで書き換えるアプローチも有効です。

リードレプリカの導入

読み取り(SELECT)が多いサービスでは、読み取り専用のレプリカを用意し、参照系のクエリをそちらに逃がすことでプライマリDBの負荷を下げられます。管理画面やレポート機能など、多少の遅延(レプリケーションラグ)が許容できる箇所から移行するのが安全です。

キャッシュの導入

頻繁に参照されるが更新頻度の低いデータは、Redis等のキャッシュ層に載せることでDBへのアクセス自体を減らせます。ページ全体のキャッシュ、断片(フラグメント)キャッシュ、クエリ結果のキャッシュなど、キャッシュ対象の粒度は既存の実装に合わせて選定します。キャッシュ導入は「何を・いつ無効化するか」の設計を誤るとデータ不整合の原因になるため、既存コードの改修と同様に段階的に導入するのが安全です。テーブル構造自体に無理がある場合はDB設計を安全に見直す進め方もあわせてご覧ください。

スケールアップの前にやるべきこと

「DBサーバーのスペックを上げれば解決するのでは」という相談もよく受けますが、順番を間違えると効果の薄い投資になります。

  • 原因を特定せずにスペックを上げても、非効率なクエリはそのまま非効率です。CPUやメモリを増強しても、Seq Scanで全件走査しているクエリは、データ量の増加とともにまた遅くなります
  • スペックアップは即効性はあるが根本解決ではない応急処置と捉えるべきです。緊急対応としてスペックを上げつつ、並行してクエリの根本改善を進める、という順番であれば有効です
  • インデックス・クエリ改善・キャッシュといった打ち手を一通り検討した上でなお不足する場合に、初めてスケールアップやシャーディングのような大掛かりな構成変更を検討します

コストをかける前に、まず「今のDBを正しく使えているか」を確認することが、結果的に最も安価で効果の高い改善ルートになります。

外注する場合に確認すべきこと

DB周りのパフォーマンス改善を外部チームに依頼する際は、次の点を確認しておくと安心です。

  • 原因特定のプロセスを踏むか: いきなり「インデックスを追加します」「スペックを上げましょう」と提案してくるのではなく、スロークエリログとEXPLAINでの調査工程が計画に含まれているか
  • 本番影響を考慮した手順か: インデックス作成やスキーマ変更が、本番トラフィックをロックしない手法(CONCURRENTLY等)で計画されているか
  • 改善前後の数値を計測するか: 「体感で速くなった」ではなく、改善前後のレスポンスタイムやスロークエリの発生件数を数値で比較できる計測がセットになっているか

技術スタックの詳細はデータベース関連の技術ページに、進め方全体はモダナイゼーション支援サービスにまとめています。

まとめ

  • DBボトルネックはまず「本当にDBが原因か」を切り分け、スロークエリログとEXPLAINで具体的な原因クエリを特定するところから始める
  • 典型原因はインデックス不足・N+1クエリ・接続数枯渇・ロック競合の4パターンに集約され、それぞれに応じてインデックス追加・クエリ改善・リードレプリカ・キャッシュを打ち手として選ぶ
  • スペックアップは即効性はあるが根本解決ではなく、原因特定と改善を先に行った上で不足する場合の最終手段と位置づける

「サービスが重い」という体感はあるが原因を特定できていない、という段階でもご相談いただけます。torcheeesでは既存プロダクト改善の診断と、継続的な改善支援を提供しています。まずはお問い合わせフォームから現状を聞かせてください。

プロダクト開発の相談をする

開発相談をする
簡易見積もり