Explain
データベース EXPLAIN 指令結果値の詳細な解説。
MySQL EXPLAIN
select_type の詳細
| 種類 | 説明 |
|---|---|
| SIMPLE | 単純なクエリ、サブクエリや UNION を含まない |
| PRIMARY | 最も外側の SELECT クエリ |
| UNION | UNION の2番目以降の SELECT |
| DEPENDENT UNION | 外部クエリに依存する UNION の SELECT |
| UNION RESULT | UNION クエリの結果セット(一時テーブル) |
| SUBQUERY | SELECT 句、WHERE、HAVING 句の最初のサブクエリ |
| DEPENDENT SUBQUERY | 外部クエリに依存するサブクエリ |
| DERIVED | 派生テーブル(FROM 句のサブクエリ) |
| MATERIALIZED | マテリアライズドサブクエリ、MySQL 5.6+ の最適化 |
| UNCACHEABLE SUBQUERY | キャッシュ不可のサブクエリ |
| UNCACHEABLE UNION | キャッシュ不可の UNION |
type の詳細(効率の高い順)
| 種類 | 説明 |
|---|---|
| system | テーブルに1行のみ(const の特例) |
| const | 主キーまたはユニークインデックスの等値クエリ、最大1行 |
| eq_ref | 主キーまたはユニーク非NULL インデックスの結合クエリ |
| ref | 非ユニークインデックススキャン |
| fulltext | 全文インデックスを使用 |
| ref_or_null | ref と同様だが NULL 値も検索 |
| index_merge | インデックスマージ最適化、複数インデックスを使用 |
| unique_subquery | IN サブクエリの最適化 |
| index_subquery | unique_subquery と同様だが非ユニークインデックス |
| range | 指定範囲のみスキャン |
| index | 全インデックススキャン |
| ALL | 全テーブルスキャン、効率最低 |
Extra フィールドの詳細
| 値 | 説明 |
|---|---|
| Using index | カバリングインデックス、インデックスのみ使用 |
| Using where | WHERE 句でフィルタリング |
| Using temporary | 一時テーブルを作成 |
| Using filesort | 外部ソートを使用、パフォーマンス問題の可能性 |
| Using join buffer | 結合バッファを使用 |
| Impossible WHERE | WHERE 句が常に false |
| Using index condition | インデックスコンディションプッシュダウン(ICP) |
| Using MRR | マルチレンジリード最適化 |
rows と filtered の解釈
rows:MySQL が検査する必要があると推定する行数。低いほど良い。
filtered:条件でフィルタリング後に残る行の割合(%)。実際に検査する行:rows × filtered%。
PostgreSQL EXPLAIN
ノードタイプの詳細
| 種類 | 説明 |
|---|---|
| Seq Scan | シーケンシャルスキャン、テーブル全体を読む |
| Index Scan | インデックススキャン、インデックス後にテーブルアクセス |
| Index Only Scan | インデックスオンリースキャン、最も効率的 |
| Bitmap Scan | ビットマップスキャン、一致行のビットマップを作成 |
| Nested Loop | ネステッドループ結合 |
| Hash Join | ハッシュ結合 |
| Merge Join | マージ結合、ソート済みデータを並行スキャン |
| Sort | ソート操作 |
| Limit | 戻す行数を制限 |
| Aggregate | 集約関数操作 |
cost 値の意味
- 単位:ディスクページ取得を基準
- startup cost:最初の出力行を生成するまでのコスト
- total cost:すべての出力行を生成する総コスト
cost=0.42..1.42 rows=10 width=14- 0.42:最初の行を生成するコスト
- 1.42:すべての10行の総コスト
- 各行の平均幅 14 バイト
actual time(EXPLAIN ANALYZE 使用時)
actual time=0.015..0.018 rows=10 loops=1- 0.015ms:最初の行を取得する時間
- 0.018ms:すべての行を処理する総時間
- loops:ノードの実行回数
- 総実際時間 = actual time × loops
バッファ情報(EXPLAIN (ANALYZE, BUFFERS) 使用時)
| 指標 | 説明 |
|---|---|
| shared hit | 共有バッファから読み込んだブロック数 |
| shared read | ディスクから共有バッファに読み込んだブロック数 |
| shared dirtied | クエリによって変更された共有バッファブロック数 |
| shared written | 共有バッファからディスクに書き込んだブロック数 |
| temp read/written | 一時ファイルの読み書き |
Filter と Index Cond の違い
- Index Cond:インデックスで直接一致行を検索、より効率的
- Filter:行を取得後にフィルタリング
MongoDB EXPLAIN
verbosity モード
| モード | 説明 |
|---|---|
| queryPlanner | デフォルト、クエリ計画のみ表示 |
| executionStats | 実行統計を含む |
| allPlansExecution | すべての計画の統計、最も詳細 |
stage の詳細
| Stage | 説明 | パフォーマンス |
|---|---|---|
| COLLSCAN | コレクションスキャン、すべてのドキュメントをスキャン | ❌ 最低 |
| IXSCAN | インデックススキャン | ✅ 効率的 |
| FETCH | インデックスから情報取得後ドキュメントを読む | 中程度 |
| SORT | メモリソート、インデックスが使えない場合 | ⚠️ 問題の可能性 |
| LIMIT | 戻すドキュメント数を制限 | ✅ |
| SKIP | 指定数のドキュメントをスキップ | ⚠️ 大量 SKIP は問題 |
| IDHACK | _id フィールドの特別最適化 | ✅ 効率的 |
| COUNT_SCAN | インデックスでカウント | ✅ |
| PROJECTION_COVERED | すべてのフィールドがインデックスに含まれる | ✅ 最高 |
executionStats の主要指標
| 指標 | 説明 |
|---|---|
| nReturned | クライアントに返されたドキュメント数 |
| executionTimeMillis | 実行時間(ミリ秒) |
| totalKeysExamined | スキャンしたインデックスキーの総数 |
| totalDocsExamined | スキャンしたドキュメントの総数 |
クエリ効率の判断基準
最良のケース:
nReturned ≈ totalKeysExamined ≈ totalDocsExamined- インデックスカバリングクエリ:totalKeysExamined > 0 かつ totalDocsExamined = 0
- インデックス効率:totalKeysExamined / nReturned 比率は 1:1 に近いべき
- ドキュメントスキャン効率:totalDocsExamined / nReturned 比率は 1:1 に近いべき
危険信号
- 大きなコレクションでの COLLSCAN
- totalDocsExamined » nReturned(必要以上のドキュメントをスキャン)
- 高い executionTimeMillis だが低い nReturned
- SORT ステージで memUsage が memLimit に近い
クエリ最適化の典型例
COLLSCAN の改善
問題: { stage: "COLLSCAN", totalDocsExamined: 10000, nReturned: 10 }
解決: クエリ条件に適したインデックスを作成インデックス効率低下
問題: { stage: "IXSCAN", totalKeysExamined: 5000, nReturned: 10 }
解決: より精確な複合インデックスを作成ソートがインデックスを使用しない
問題: { stage: "SORT", executionTimeMillis: 500, memUsage: 30000000 }
解決: ソートフィールドを含むインデックスを作成SKIP の効率問題
問題: 大量の SKIP でページネーション
解決: 前回のクエリ結果に基づく条件(例:{ _id: { $gt: lastId } })