クエリ多重実行で性能劣化が起きる原因と実践的な対策手順

同時アクセスが増える時間帯に限ってレスポンスが急激に悪化する――データベースを運用していると、この「クエリ多重実行による性能劣化」は避けて通れない課題です。単一クエリの最適化だけでは解決できず、CPU・メモリ・ディスクI/Oといったサーバーリソースの競合が根本原因になっているケースが大半を占めます。 本記事では、リソース競合がなぜ発生するのかを仕組みから掘り下げ、RDBMS別の診断SQLと段階的な改善手順を具体例つきで整理しています。 クエリ多重実行で性能劣化が発生するメカニズム データベースは同時に複数のクエリを処理する際、CPU時間・メモリバッファ・ディスクI/O帯域といった有限リソースを各セッションに分配します。同時実行数が一定の閾値を超えると、リソースの奪い合い(競合)が起き、個々のクエリの応答時間が指数関数的に悪化します。 リソース競合の3つのレイヤー レイヤー 競合対象 典型的な兆候 CPU 実行スレッド / ワーカープロセス CPU使用率100%張り付き、load average がコア数の数倍 メモリ 共有バッファ / ソートバッファ スワップ発生、OOM Killer起動、バッファキャッシュヒット率低下 ディスクI/O データファイル読み書き iowait の上昇、IOPS上限到達、WALの書き込み遅延 単一クエリが正常に動作する環境でも、同じクエリを10並列・50並列で実行すると急激に劣化する理由は、このリソース分配の上限にあります。たとえばPostgreSQLではshared_buffersのサイズが固定されているため、多数のクエリが同時にバッファプールを走査すると、キャッシュのスラッシング(頻繁な入れ替え)が起こり、本来キャッシュに乗るべきデータが追い出されてディスクI/Oが増大します。 ロック競合による連鎖的な待機 リソース競合と並んで問題になるのが、行ロックやテーブルロックによる待機の連鎖です。あるトランザクションがロックを保持したまま長時間実行されると、後続のトランザクションはロック待ちキューに入り、そのロック待ちがさらに次のトランザクションの待機を引き起こす「ロックチェーン」が形成されます。 -- PostgreSQL: 現在のロック待ち状況を確認する SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query, blocking.pid AS blocking_pid, blocking.query AS blocking_query, blocked.wait_event_type FROM pg_stat_activity AS blocked JOIN pg_locks AS bl ON bl.pid = blocked.pid AND NOT bl.granted JOIN pg_locks AS kl ON kl.locktype = bl.locktype AND kl.database IS NOT DISTINCT FROM bl.database AND kl.relation IS NOT DISTINCT FROM bl.relation AND kl.page IS NOT DISTINCT FROM bl.page AND kl.tuple IS NOT DISTINCT FROM bl.tuple AND kl.pid != bl.pid AND kl.granted JOIN pg_stat_activity AS blocking ON blocking.pid = kl.pid WHERE blocked.wait_event_type = 'Lock'; 性能劣化を引き起こす代表的な5つのパターン パターン1: 統計情報の陳腐化 データベースのクエリオプティマイザは、テーブルの行数やカラムの値の分布(統計情報)をもとに最適な実行計画を選択します。データの大量INSERT・DELETE後に統計情報が更新されないと、オプティマイザが非効率な実行計画を選び、フルテーブルスキャンやNested Loopが不要に発生します。 ...

2026年2月10日 · 3 分 · 8880 文字 · uiuifree

N+1問題によるクエリ発行数の増大を防ぐ方法|ORM別の検出・対策を徹底解説

データベースを使ったアプリケーション開発で、レスポンスが遅いと感じたことはないでしょうか。原因の多くは、気づかないうちに膨大な数のSQLクエリが発行されている N+1問題 です。 たとえばユーザー100人の一覧画面で、1回の全件取得+100回の関連テーブル参照、合計 101回 ものクエリが実行されるケースが典型的です。本来2回で済む処理が50倍以上に膨れ上がるため、データ量に比例してアプリケーション全体が劇的に遅くなります。 N+1問題とは何か リレーショナルデータベースでは、親テーブルと子テーブルを組み合わせてデータを取得する場面が頻繁にあります。N+1問題は、この関連データの取得時に発生するパフォーマンス上の欠陥です。 具体的な流れは次のとおりです。 親テーブルから全レコードを取得する(1回のクエリ) 取得した各レコードに対して、子テーブルへ個別にクエリを発行する(N回のクエリ) 合計で N+1回 のクエリが走ります。Nはレコード数なので、データが増えるほどクエリ発行数も線形に増加します。 クエリ発行数はなぜ問題になるのか クエリ1回ごとにネットワーク往復(ラウンドトリップ)が発生します。アプリケーションサーバーとデータベースサーバーが別マシンの場合、1往復あたり0.5〜2ms程度のオーバーヘッドが加わります。 レコード数 N+1時のクエリ数 JOINで解決した場合 差分(倍率) 10件 11回 1〜2回 約6〜11倍 100件 101回 1〜2回 約51〜101倍 1,000件 1,001回 1〜2回 約501〜1,001倍 10,000件 10,001回 1〜2回 約5,001〜10,001倍 ラウンドトリップだけで見ても、レコード1,000件で1秒以上のレイテンシ増が現実的に発生します。さらにデータベース側でもクエリパース・実行計画の策定がクエリ数だけ繰り返されるため、CPU負荷・コネクションプールの圧迫・ロック競合などが連鎖的に起こります。 SQLレベルで見るN+1問題の発生メカニズム ORMを使わない素のSQLでも、N+1問題の構造を理解しておくと対策が打ちやすくなります。ブログ記事とコメントのテーブルで見てみましょう。 -- テーブル定義 CREATE TABLE posts ( id SERIAL PRIMARY KEY, title VARCHAR(255) NOT NULL, body TEXT ); CREATE TABLE comments ( id SERIAL PRIMARY KEY, post_id INTEGER REFERENCES posts(id), content TEXT NOT NULL ); N+1が発生するクエリパターン -- 1回目:全記事を取得 SELECT * FROM posts; -- 記事ごとにコメントを取得(N回) SELECT * FROM comments WHERE post_id = 1; SELECT * FROM comments WHERE post_id = 2; SELECT * FROM comments WHERE post_id = 3; -- ... post_id = N まで繰り返し 記事が500件あれば、合計501回のクエリが発行されます。 ...

2026年2月10日 · 4 分 · 9989 文字 · uiuifree

Webパフォーマンスとは?基本概念・測定指標・改善手法を体系的に解説

ページの読み込みに3秒以上かかると、モバイルユーザーの53%が離脱するというデータがあります(出典: Google / SOASTA Research, 2016)。Webパフォーマンスは単なる技術的な課題ではなく、ビジネス成果に直結する要素です。 Webパフォーマンスとは、Webサイトやアプリケーションがユーザーのリクエストに対してどれだけ速く・スムーズに応答できるかを示す総合的な品質指標です。表示速度だけでなく、操作への応答性、レイアウトの安定性、そして「体感的な速さ」も含まれます。 Webパフォーマンスを構成する4つの側面 Webパフォーマンスは大きく分けて4つの側面から評価できます。 読み込み速度(Loading Performance) ブラウザがサーバーからリソースを取得し、画面に描画するまでの時間です。HTML・CSS・JavaScript・画像・フォントなど、必要なリソースをすべてダウンロードして処理する時間が対象となります。 DNS解決、TCP接続、TLSハンドシェイク、サーバー応答、リソース転送という一連のネットワーク処理が順番に実行されるため、各段階での遅延が累積します。 操作応答性(Interactivity) ユーザーがボタンをクリックしたりフォームに入力したりした際に、どれだけ速くUIが反応するかを示します。JavaScriptの実行がメインスレッドをブロックしていると、操作に対する応答が遅れ、サイトが「固まった」ように感じられます。 表示の安定性(Visual Stability) ページのコンテンツが読み込み中に予期せず移動しないかどうかです。広告や画像の遅延読み込みによってテキストが突然ずれると、誤タップや読んでいた箇所を見失う原因になります。 知覚的パフォーマンス(Perceived Performance) 実際の速度とは別に、ユーザーが「速い」と感じるかどうかです。プログレスバーやスケルトンスクリーン(コンテンツ領域の仮表示)を使うことで、同じ読み込み時間でも体感速度を向上させることができます。 Core Web Vitals ― Googleが定めた3つの品質指標 GoogleはWebパフォーマンスの評価基準としてCore Web Vitalsを定義しており、検索ランキングのシグナルとしても使用しています。2024年3月12日にFID(First Input Delay)がINP(Interaction to Next Paint)に正式に置き換えられ、現在の3指標は以下のとおりです。 指標 測定対象 良好 改善が必要 不良 LCP(Largest Contentful Paint) 最大コンテンツの描画時間 2.5秒以内 2.5〜4.0秒 4.0秒超 INP(Interaction to Next Paint) 操作から画面更新までの遅延 200ms以内 200〜500ms 500ms超 CLS(Cumulative Layout Shift) レイアウトのずれの累積量 0.1以下 0.1〜0.25 0.25超 LCP ― ページの「見た目の速さ」を測る LCPは、ビューポート内に表示される最も大きなコンテンツ要素(ヒーロー画像、見出しテキスト、動画のサムネイルなど)が描画完了するまでの時間です。ユーザーが「ページが表示された」と感じるタイミングに近い指標であり、2.5秒以内が推奨されています。 LCPの遅延原因として多いのは、サーバー応答時間(TTFB)の長さ、レンダリングブロックするCSS/JS、大容量画像の最適化不足、クライアントサイドレンダリングによる描画遅延の4つです。 INP ― すべての操作の応答性を評価する INPは、ページ上で行われたすべてのインタラクション(クリック・タップ・キー入力)のうち、応答が最も遅かったもの(正確にはP98 = 98パーセンタイル)の遅延時間を測定します。 ...

2026年2月10日 · 2 分 · 7641 文字 · uiuifree