システムのパフォーマンス改善において、「データベース(DB)クエリの最適化」は避けて通れない重要なテーマです。
しかし、いざ改善に取り組もうとすると、 「Slow Query ログが多すぎて、どこから手を付ければいいか分からない」 「苦労してSQLをチューニングしたのに、システム全体の負荷はあまり下がらなかった」 といった 「優先順位付けの壁」 にぶつかることはないでしょうか?
また、クラウド環境においては、非効率なクエリは単なるレスポンス遅延にとどまらず、 CPU負荷やI/O課金の増大といった「クラウドコストの無駄」 に直結します。
本記事では、New Relic APM を活用し、 「勘と経験」ではなく「定量的なデータ」に基づいて、最短距離でDBパフォーマンスを改善するための実践的なワークフロー を解説します。
数あるトランザクションの中から「ビジネスインパクトの大きい改善対象」をどう絞り込み、N+1問題やSlow Queryをどう解決し、その効果をどう検証するか。今日から使える具体的な手順を紹介していきます。
最新のアップデートの詳細はこちら
New Relic アップデート一覧
無料のアカウントで試してみよう!
New Relic フリープランで始めるオブザーバビリティ!
DBクエリ改善が難しい理由
DBクエリのパフォーマンス改善は、多くのシステムで重要なテーマである一方、実務では「どこから手を付ければよいのか分からない」状態に陥りがちです。
その理由の一つは、問題が複数の形で同時に存在していることです。
例えば以下のようなケースが混在します。
- 特定のトランザクションでN+1問題によりDBアクセス回数が異常に多い
- 呼び出し回数は少ないが単発のSQLが重くDB時間を占有している
- 一部の時間帯やバッチ処理実行時のみDB負荷が急増する
これらはすべて「DBが遅い」という同じ症状として現れますが、取るべき対策はまったく異なります。
また、New Relic APMを導入している環境であっても、次のような状況に心当たりがある方は多いのではないでしょうか。
- Transaction一覧やTraceを開いたが、情報量が多すぎて判断できない
- Slow Queryを眺めているうちに、どれが重要なのか分からなくなる
- 「とりあえず遅そうなSQL」を改善したが、全体の改善効果が見えない
これはツールの問題ではなく、調査の順序が整理されていないことが原因であるケースがほとんどです。
DBクエリ改善を効率的に進めるためには、
- システム全体の中で
- どのトランザクションが
- どの種類のDB負荷を生んでいるのか
を定量的に切り分ける必要があります。
本記事では、この課題に対して New Relic APM の Transaction Event(Eventデータ)を使い、全体から効率よくスクリーニングする方法を紹介していきます。
DBクエリ改善がクラウドコスト削減に繋がる理由
DBクエリのパフォーマンス改善は、単にアプリケーションのレスポンスを速くするためだけの取り組みではありません。クラウド環境においては、DBクエリの効率がそのままコストに跳ね返ってきます。
多くのマネージドDBサービスでは、CPU使用率、ディスクI/O、同時接続数といった指標をもとにインスタンスサイズや台数が決まります。つまり、無駄なDB処理が多いほど、より高価なリソースが必要になる構造です。
例えば、次のような状況は珍しくありません。
- N+1問題により、1リクエストあたりのDBアクセス回数が増加
- 非効率なSQLにより、クエリ1回あたりの実行時間が長期化
- DB処理の遅延により、アプリケーション側でコネクションが滞留
これらが積み重なると、
- DBのCPU使用率が高止まりする
- IOPSが増加し、ストレージコストが上がる
- コネクション枯渇を避けるためにインスタンスをスケールアップする
といった形で、クラウドコストの増加を招きます。
重要なのは、こうしたケースの多くが「アクセス量が増えたから仕方ない」のではなく、 「同じ処理を非効率に実行しているだけ」 である点です。
トランザクション単位で見ると、一部の処理が過剰にDBリソースを消費しているだけで、システム全体としてはまだ余力がある、という状況も少なくありません。
このような場合、
- DBインスタンスをスケールアップする前に
- リードレプリカを追加する前に
改善すべきクエリやトランザクションを正しく特定できれば、コストを増やさずに性能問題を解消できる可能性があります。
だからこそ、DBクエリ改善では「とりあえず遅いSQLを直す」のではなく、
- どのトランザクションが
- どれだけDBリソースを消費しており
- 改善した場合、どの程度の効果が見込めるのか
を把握した上で、優先順位を付けることが重要です。
この優先順位付けを実現するために、New Relic APMのEventデータを使ったスクリーニング戦略を紹介します。
New Relic APMのEventデータを使ったスクリーニング戦略
New Relic APMを導入している環境では、トランザクションごとの処理状況がTransaction Event(Eventデータ) として自動的に蓄積されています。
このEventデータの大きな特徴は、「システム全体を俯瞰しながら、定量的に比較できる」 点にあります。
なぜ Transaction Event なのか
TraceやDistributed Tracingは非常に強力な分析手段ですが、その分、対象をある程度絞り込んだ状態で使うのが理想です。全トランザクションをTraceで一つずつ確認するのは、工数的にも現実的ではありません。
そこで有効なのが、Eventデータを使ったスクリーニング という考え方です。
本記事で紹介するスクリーニング戦略は、次のような流れを前提としています。
- システム全体を対象に
- Transactionイベントを集計し
- DB負荷が大きいトランザクションを定量的に抽出する
このアプローチを取ることで、
- 「なんとなく遅そう」
- 「以前問題になったことがある」
といった感覚的な判断ではなく、数値に基づいた改善対象の選定が可能になります。
Transaction のどの指標に注目するのか
Transaction Eventには、DBクエリ改善に直結する有用な情報が含まれています。
本記事では、特に次の3つの指標に注目します。
- duration : トランザクション全体の処理時間
- databaseCallCount : トランザクション内で実行されたDB呼び出し回数
- databaseDuration : DB処理に費やされた合計時間
これらを組み合わせて見ることで、
- DBアクセス回数が異常に多いトランザクション(N+1候補)
- DB処理時間が長く、クエリ自体が重いトランザクション
- 回数・時間の両方でDBリソースを消費しているトランザクション
を切り分けることができます。
重要なのは、この段階では 「原因を特定する」ことが目的ではない という点です。
Transaction Event を利用する目的
Event データを使ったスクリーニングの目的は、あくまで 「調査・改善すべきトランザクション候補を絞ること」 です。原因の特定やSQLレベルの分析は、この後に APM UI や Trace で行います。
この順序を意識することで、
- 調査対象が絞れないまま時間を消費する
- 改善効果の小さいクエリに手を付けてしまう
といった無駄を避けることができます。
Transaction Eventで「改善対象トランザクション」を絞り込む
DBクエリ改善に取り組む際、最初にやるべきことは 「どのトランザクションを調査対象にするか」 を決めることです。
Transaction Event を使って改善対象トランザクションをスクリーニングし、数個に絞り込んでから APM UI で深掘りする流れを取ります。
この段階で行うのは 原因特定ではなく、優先順位付け です。
以下のNRQLでは、実行頻度・処理時間・DB処理時間・DB呼び出し回数・DB時間比率 を同時に取得します。
New Relicの『Query Your Data』(Query Builder) を開き、以下のクエリを貼り付けて実行してください。
FROM Transaction
SELECT
(count(*) * average(duration)) AS time_consumed_sec,
(average(databaseDuration / duration)) * 100 AS db_ratio_pct,
count(*) AS exec_count,
average(duration) AS avg_duration_sec,
percentile(duration, 95) AS p95_duration_sec,
max(duration) AS max_duration_sec,
average(databaseDuration) AS avg_db_sec,
percentile(databaseDuration, 95) AS p95_db_sec,
max(databaseDuration) AS max_db_sec,
average(databaseCallCount) AS avg_db_calls,
percentile(databaseCallCount, 95) AS p95_db_calls,
max(databaseCallCount) AS max_db_calls
WHERE appName LIKE '%' // YOUR APP NAME
AND databaseCallCount IS NOT NULL
SINCE 24 hours ago
FACET appName, name
LIMIT 50
この結果テーブルが、
改善対象トランザクションの候補一覧になります。
どの列をどう使って判断するか
このNRQLは列数が多いため、各列の役割を明確にして見ることが重要です。
① 改善優先度を決める
• time_consumed_sec
• 実行頻度 × 平均処理時間
• システム全体で最も時間を消費しているトランザクション
• まず直す候補はここから選ぶ
「一番遅いトランザクション」ではなく 「全体への影響が最も大きいトランザクション」 を選ぶのがポイントです。
FROM Transaction
SELECT
(count(*) * average(duration)) AS time_consumed_sec
WHERE appName LIKE '%' // YOUR APP NAME
AND databaseCallCount IS NOT NULL
SINCE 24 hours ago
FACET appName, name
LIMIT 50
② N+1問題の疑い(回数型)
• p95_db_calls / max_db_calls
• DB呼び出し回数が多いトランザクション
• ループ内クエリやN+1問題の可能性
time_consumed_sec が高いものほど、改善による効果が全体に効きやすくなります。
FROM Transaction
SELECT
average(databaseCallCount) AS avg_db_calls,
percentile(databaseCallCount, 95) AS p95_db_calls,
max(databaseCallCount) AS max_db_calls
WHERE appName LIKE '%' // YOUR APP NAME
AND databaseCallCount IS NOT NULL
SINCE 24 hours ago
FACET appName, name
LIMIT 50
③ Slow Queryの疑い(時間型)
• p95_db_sec / max_db_sec
• DB処理時間が長いトランザクション
• 単発でも重いSQL、インデックス不足、ロックなどの可能性
FROM Transaction
SELECT
average(databaseDuration) AS avg_db_sec,
percentile(databaseDuration, 95) AS p95_db_sec,
max(databaseDuration) AS max_db_sec
WHERE appName LIKE '%' // YOUR APP NAME
AND databaseCallCount IS NOT NULL
SINCE 24 hours ago
FACET appName, name
LIMIT 50
④ DB起因かどうかの切り分け(補助指標)
• db_ratio_pct(DB実行時間比率)
• トランザクションの処理時間のうち、DBが占める割合
この値は 優先順位付けには使いません。
用途は「このトランザクションの遅さは、DBを深掘りすべきか?」を判断するためです。
例:
• db_ratio_pct が高い → DBクエリ改善(N+1 / Slow Query)を調査する価値が高い
• db_ratio_pct が低い → アプリロジックや外部APIが原因の可能性も考慮する
⑤ maxの扱いについて
max は単発の外れ値検知用です。
優先順位付けは、time_consumed_sec、p95 系の値を基本にします。
max だけが高く、exec_count が極端に少ない場合は、単発事象として優先度を下げても問題ありません。
この段階で分かること・分からないこと
分かること
• 改善対象として優先すべきトランザクション
• N+1(回数型)か Slow Query(時間型)かの傾向
• DB起因の可能性が高いかどうか
分からないこと(次のステップで確認)
• 具体的なSQL
• 実行計画やインデックスの問題
• どのクエリがN+1を起こしているか
※ databaseCallDuration / databaseCallCount には Redis 等の Datastore が含まれる場合がありますが、この段階では 「 DB 負荷が高いトランザクションを拾う」 ことが目的なので問題ありません。
次のステップ
ここまでで、「調査すべきトランザクション」が数個に絞れた状態になります。
次は、 これらのトランザクションを New Relic APM の UI で深掘りし、 効率よく原因を特定していきます。
APM UIを使って N+1 問題・Slow Query を調査する
前章で、Transaction Event を使って 「調査すべきトランザクション」が数個に絞れた状態 になっています。
ここからは NRQL を追加で書くのではなく、 New Relic APM の UI をそのまま活用して原因を特定 していきます。
ポイントは次の2つです。
• N+1 問題はトランザクションの Transaction Trace で確認する
• Slow Query は Databases 画面で改善のヒントを活用する
この役割分担を意識すると、調査が最短で進みます。
Transactions画面で対象トランザクションを開く
まず、前章のスクリーニングで特定したトランザクション名を元に、APM の Transactions 画面から該当トランザクションを開きます。
ここで確認するのは次の点です。
• トランザクション全体の処理時間の内訳
• DB(Datastore)が処理時間の大部分を占めているか
この時点で、 DB時間が支配的 という傾向が見えていれば、 DBクエリ改善に着手する判断として十分 です。
Breakdown table に1回の処理でどの処理が支配的なのか、何回実行されているのかが表示されています。このように Database の処理が何度も繰り返しコールされているケースでは N+1 問題が含まれている可能性が高いです。
N+1問題の調査(DB呼び出し回数が多い場合)
p95_db_calls, max_db_calls が高かったトランザクションは、 N+1問題や無駄な繰り返しクエリ の可能性があります。
この場合は、トランザクションの Transaction trace で実際にどのように処理されているのかを確認します。
Time consumed by segment (ms) にこの処理の中でどのセグメントが何回実行されていて何秒かかっているのかをまとめて表示しています。N+1 問題の場合は特定の Database アクセスが多くなります。
見るポイント
• 同じ種類のDatastoreアクセスが連続して並んでいないか
• ループ処理の中でDBアクセスが発生していないか
• 同一テーブルへのクエリが大量に発行されていないか
この段階で 「どの処理が繰り返されているか」 を把握できれば、アプリケーション側の修正ポイントはかなり明確になります。
Slow Queryの調査(DB処理時間が長い場合)
p95_db_sec, max_db_sec が高かったトランザクションは、 単発でも重いSQL(Slow Query) を含んでいる可能性があります。
FROM Transaction
SELECT
(count(*) * average(duration)) AS time_consumed_sec,
average(databaseDuration) AS avg_db_sec,
percentile(databaseDuration, 95) AS p95_db_sec,
max(databaseDuration) AS max_db_sec
WHERE appName LIKE '%' // YOUR APP NAME
AND databaseCallCount IS NOT NULL
SINCE 24 hours ago
FACET appName, name
LIMIT 50
この場合は、NRQLでSQLを探すのではなく、 APM > Databases > Slow SQL traces をそのまま使うのが最も効率的 です。
対象のトランザクションを Transactions で確認すると Breakdown table に長時間かかっているクエリを確認することができます。

セグメントのリンクから APM > Databases へ直接飛ぶことができます。

Slow SQL traces は指定した基準を超えて時間がかかった SQL クエリの詳細情報を記録し、ボトルネックの特定を支援する機能です。

実行された SQL クエリの詳細情報を収集し、クエリや実行時間の情報を確認できます。また、MySQL や PostgreSQL を使用している場合は、Explain や Stack trace の情報を収集可能です。

Query analysis のヒント情報を活用することでスロークエリの改善が見込めます。
たとえば、このクエリの場合は下記のようことがわかります。
-
見かけ上の安心: 主キー(PRIMARY)を使っていますが、特定の行をピンポイントで検索しているわけではありません
-
実態は全件確認: インデックスの先頭から末尾までをすべて読み込む「全件走査(Full Scan)」に近い処理が行われています
-
パフォーマンスへの影響: 検索条件(WHERE句)に適したインデックスがないため、結果としてテーブル内の約 24 万行すべて を無駄にスキャンしており、動作が重くなっています
このような改善ポイントを提案してくれるため、提案に沿ってindex の追加設定やクエリの見直しを実施することでスロークエリの改善が実現できます。
改善効果の検証:Change Tracking の活用
原因を特定し、アプリケーションの修正やインデックスの追加を行ったら、必ず 「実際にどれくらい改善したのか」 を検証します。
「なんとなく速くなった気がする」で終わらせず、数値で改善効果を証明することは、エンジニアの成果を示す上でも、クラウドコスト削減のROI(費用対効果)を測る上でも非常に重要です。
ここで役に立つのが Change Tracking 機能です。
Change Tracking は API や CLI、CI/CD ツールから簡単に送信できます。設定方法はこちらの公式ドキュメントを参照してください。
New Relic の Change Tracking を利用すると、アプリケーションのデプロイや設定変更のタイミングをチャート上に縦線(マーカー)として記録できます。これを使うことで、「修正前(Before)」と「修正後(After)」のパフォーマンス変化を一目で比較確認できます。
検証時のチェックポイントは以下の通りです。
-
Transaction Time(応答時間)は短縮されたか?
- Change Tracking のマーカーを境に、平均応答時間や99パーセンタイル値が下がっているかを確認します
-
Database Call Count(DB呼び出し回数)は減ったか?
- N+1対策を行った場合、リクエストあたりのクエリ数が劇的に減っているはずです
-
Throughput(スループット)への影響は?
- 処理が軽くなったことで、同じリソースでより多くのリクエストをさばけるようになっているか確認します
-
エラーは発生していないか?
- 修正によるデグレ(副作用)がないかも同時に監視します。Change Tracking はエラー率の変化とも相関を見ることができます
このように「リリース直後にグラフが良い方向に変化したこと」を視覚的に確認できて初めて、DBクエリ改善は完了します。
まとめ:推測ではなく「計測」で改善する
DBクエリのパフォーマンス改善は、闇雲にログを漁ったり、勘でインデックスを貼ったりする作業ではありません。
本記事で紹介したフローを振り返ります。
-
スクリーニング(Eventデータ / NRQL)
- 「なんとなく遅い」ではなく、 「システム全体で最も時間を消費している(time_consumed_sec)」 トランザクションを特定する
- N+1(回数型)か、Slow Query(時間型)かのあたりを付ける
-
深掘り調査(APM UI)
- Transaction Trace でループ処理や無駄な呼び出し(N+1)を特定する
- Slow SQL traces で実行計画やインデックス不足(Slow Query)を特定する
-
検証(Change Tracking)
- リリース前後の数値を比較し、改善効果とコスト削減への貢献を定量化する
この「計測 → 特定 → 改善 → 検証」のサイクルを回すことで、限られたリソースの中で最大のパフォーマンス改善効果を得ることができます。
クラウドコストの削減やユーザー体験の向上のために、ぜひ New Relic のデータを活用した 「根拠のあるDBクエリ改善」 に取り組んでみてください。
New Relicでは、新しい機能やその活用方法について、QiitaやXで発信しています!
無料でアカウント作成も可能なのでぜひお試しください!
New Relic株式会社のX(旧Twitter) や Qiita OrganizationOrganizationでは、
新機能を含む活用方法を公開していますので、ぜひフォローをお願いします。
無料のアカウントで試してみよう!
New Relic フリープランで始めるオブザーバビリティ!











