データベースチューニングにおいて「どのSQLが遅いのか」だけでなく、「どの処理がボトルネックなのか」を正しく把握することは非常に重要です。
そのための基本ツールが**実行計画(EXPLAIN PLAN)**です。
本記事では、Oracleを例に実行計画の見方とボトルネックの探し方を、初心者でも理解できるように解説します。
✅ EXPLAIN PLANとは?
SQLを実行する際、Oracleが内部的に考える**最適な実行手順(アクセス方法)**を表示する機能です。
実行計画を見ることで、次のようなことがわかります。
-
テーブルにアクセスする順番
-
インデックスを使っているかどうか
-
結合(JOIN)の方式
-
フルスキャンが走っているか
-
コスト(予測負荷)
✅ 実行計画の取得方法
▼ 方法1:EXPLAIN PLAN文を使う
▼ 方法2:SQL Developer で「実行計画」ボタン
GUI環境ではワンクリックで参照できます。
✅ Oracle実行計画の基本構造
実行計画は階層構造で、上から順に処理が行われます。
インデントが深いほど「その処理の中で実行される詳細処理」です。
例:
✅ よく出るOperationと解釈ポイント
| Operation | 説明 | 見どころ |
| TABLE ACCESS FULL | テーブルの全件スキャン | 大量データで出たら要注意 |
| TABLE ACCESS BY INDEX ROWID | インデックス参照後にROWIDアクセス | 最適パターンの一つ |
| INDEX UNIQUE SCAN | 主キー・ユニークインデックス検索 | 高速 |
| INDEX RANGE SCAN | 範囲検索 | 効率的だが条件次第 |
| HASH JOIN | ハッシュ表でJOIN | 大量データ向き、メモリ消費 |
| NESTED LOOPS | 小規模データに適したJOIN | 結合相手の行数が多いと遅い |
| SORT ORDER BY | 並び替え | 必要ならOK、無駄がないか確認 |
✅ ボトルネックの探し方
① TABLE ACCESS FULL に注意
-
条件にインデックスが効いていない可能性
-
大規模テーブルで特に危険
対策:
-
WHERE句に使う列にインデックス追加
-
不必要なSELECT *を避ける
-
ファンクションインデックス
② JOIN方式を確認
| JOIN方式 | 特徴 | 適したケース |
| NESTED LOOPS | 小テーブル to 大テーブルに◎ | OLTP向き |
| HASH JOIN | 大量データ向き、高速 | DWH向き |
| MERGE JOIN | 並び替え前提、ソート負荷 | ソート後結合 |
Nested Loops × 大量データ → 遅い可能性
③ コスト(COST)とROWSを確認
| 項目 | 意味 |
| ROWS | 見積もられる行数 |
| COST | Oracleが見積もる負荷指数 |
| BYTES | データ量 |
COSTが極端に高い行がボトルネック候補。
④ SORTが多い場合
ORDER BY や DISTINCTが多いと遅くなる
対策:
-
必要な場面以外でDISTINCT使用しない
-
ORDER BYの列にインデックス
✅ 実例:遅いSQLの典型パターン
問題点
改善例
✅ チューニングの基本手順まとめ
| ステップ | 内容 |
| 1 | 実行計画を見る |
| 2 | TABLE FULL SCANをチェック |
| 3 | JOIN方法確認(Nested Loops vs Hash Join) |
| 4 | コスト高い箇所を特定 |
| 5 | インデックス/SQL修正 |
✅ まとめ
-
EXPLAIN PLANはSQLの動作設計図
-
インデックス利用とJOIN方式を重視
-
FULL SCANと高コスト行は警戒
-
必要な列だけ取得し、関数利用に注意
SQLチューニングは**「まず実行計画を見る」**がスタートです。
慣れるほど読み解きが早くなり、効率的な分析ができるようになります。
1. インデックスヒントとは?
SQLの**インデックスヒント(INDEX HINT)**とは、データベースに対して「このテーブルでは特定のインデックスを使用して実行してほしい」と明示的に指示するための構文です。
通常、DBエンジン(オプティマイザ)が自動で最適な実行計画を選びますが、統計情報が古い・複雑な条件句などの場合に誤ったインデックスを選ぶことがあります。
そのようなとき、開発者が明示的にインデックスを指定して最適化を誘導するのがINDEX HINTの目的です。
2. 一般的な書き方
Oracleの場合
MySQLの場合
SQL Serverの場合
| データベース | ヒント指定方法 | 備考 |
| Oracle | /*+ INDEX(テーブル名 インデックス名) */ | ヒント句はコメント形式 |
| MySQL | USE INDEX (インデックス名) | FORCE INDEX / IGNORE INDEX も可 |
| SQL Server | WITH (INDEX(インデックス名)) | テーブルヒントとして指定 |
3. MySQLにおけるINDEX HINTの種類
| 種類 | 意味 | 使用例 |
| USE INDEX | 指定したインデックスを優先的に使用 | USE INDEX (idx_col1) |
| FORCE INDEX | 指定インデックスを強制的に使用 | FORCE INDEX (idx_col1) |
| IGNORE INDEX | 指定インデックスを無視して検索 | IGNORE INDEX (idx_col1) |
4. 実行計画を確認する
インデックスヒントを付与したら、実際に利用されているかを確認することが重要です。
MySQL
Oracle
5. 使用上の注意点
| 注意点 | 内容 |
| 1. 過信しない | ヒントは一時的なチューニングであり、将来的な統計情報変化で逆効果になることも。 |
| 2. 実行計画を常に確認 | ヒント適用後は EXPLAIN で確認すること。 |
| 3. SQL互換性に注意 | 各DBMSで構文が異なるため、移植性が下がる。 |
| 4. ヒント指定よりも統計情報更新が基本 | 統計情報を更新することで自動最適化が正しく働くことも多い。 |
6. 実用例:複合インデックスを明示的に使用
以下のように複数条件を持つ検索で、DBが誤ったインデックスを選ぶ場合に有効です。
このように、複合インデックスの指定順序に合わせてヒントを指定することで、不要な全件スキャンを防ぐことができます。
7. まとめ
| ポイント | 内容 |
| 自動最適化が基本 | まずはDBエンジンに任せるのが原則 |
| ヒントは最終手段 | 特定クエリで誤選択時のみ使用 |
| EXPLAINで検証 | 効果を数値で確認 |
| 統計情報更新も忘れずに | オプティマイザの精度を保つために重要 |
GCとは何か
Java で開発をしていると、よく耳にする「GC(Garbage Collection)」。
これは 不要になったオブジェクトを自動で回収してメモリを解放する仕組み のことです。C言語のように手動で free() を呼ぶ必要はなく、Java VM が裏側でメモリ管理を行います。
ざっくり構造・最近のGC
-
世代別回収:Eden/Survivor(若世代)→ Old(老世代)
-
Minor GC:Edenが埋まったら短命オブジェクト中心に回収
-
Major/Full GC:Oldが逼迫、断片化、クラス/メタ領域逼迫などで広域回収
-
既定GC:G1GC(Java 9+)。低停止要求は ZGC / Shenandoah も選択肢
主なトリガ
“悪い例 → 良い例”で学ぶメモリ/GC対策
1) 無制限キャッシュ(静的Map地獄)
悪い例
ポイント:上限なしは必ずOldを膨らませる。キャッシュは 容量・期限・エビクションを設計。
2) リスナ/コールバック未解除
悪い例
3) ThreadLocal の放置(プールスレッドに張り付く)
悪い例
良い例(finallyで確実に除去)
4) System.gc() 乱用
悪い例
5) ラムダ/内部クラスが外側(巨大オブジェクト)をキャプチャ
悪い例
良い例(必要最小限のデータだけ渡す・static化)
ポイント:キャプチャ=保持。意図せず大物を延命していないか疑う。
6) ループ内の大量一時オブジェクト
悪い例
良い例(StringBuilder再利用・ボクシング回避)
7) finalize/Cleaner頼み(遅延・不確実)
悪い例
8) クラスローダ・アプリ再デプロイ時のリーク
悪い例
良い例(クラスローダ境界を越える参照を断つ)
9) 巨大配列・Humongous割当ての長期保持(G1)
悪い例
良い例(分割・ストリーミング・寿命短縮)
ポイント:巨大ブロックは断片化と回収コスト増の温床。
10) 無制限のキュー/バッファ
悪い例
良い例(有界+バックプレッシャ)
GCログ・計測の始め方(JDK 9+)
-
まずはコードの割当て削減 → その後にヒープ/GC調整
-
監視:jcmd <pid> GC.heap_info / jstat -gc <pid> 1000
-
ボトルネック特定:JFR(Java Flight Recorder) で割当てホットスポットを把握
-
必要なら ZGC/Shenandoah も評価(レイテンシ目標に応じて)
実務チェックリスト(配布推奨)
-
System.gc() を禁止/抑制
-
キャッシュ・キューは有界+期限
-
ThreadLocal は finally で remove
-
リスナ/コールバックは確実に解除(AutoCloseable化が効く)
-
ループ内の一時オブジェクトを減らす(Builder再利用/ボクシング回避)
-
巨大配列は分割・短命化
-
クラスローダ境界を跨ぐ静的参照禁止、Executor停止・ドライバ解除
-
try-with-resourcesでオフヒープ即時解放
-
GCログ/JFRで事実ベースに調整
-
目標停止時間(例:MaxGCPauseMillis)を定めて検証
まとめ
GCは“自動”でも“万能”ではありません。
「GCが働きやすいコード」(不要参照を残さない・波及して大物を掴ませない・ピークメモリを避ける)を心がけ、ログ/計測で改善ループを回すのが最短距離です。
「駑馬十駕」を信念に IT系情報を中心に調べた事をコツコツ綴っています。