実行計画の見方

EXPLAIN PLAN FOR
SELECT o.id, c.name
  FROM orders o
  JOIN customers c ON o.cust_id = c.id
 WHERE o.status = 'OPEN';

------------------------------------------
| Id | Operation              | Cost |
------------------------------------------
|  0 | SELECT STATEMENT       |  120 |
|  1 |  HASH JOIN             |  120 |
|  2 |   TABLE ACCESS FULL    |   80 |
|  3 |   INDEX RANGE SCAN     |   20 |
------------------------------------------

読むときの観点

  1. ネストは下から上へ実行される
  2. FULL SCAN は意図したものか?
  3. INDEX RANGE / UNIQUE / SKIP の別
  4. JOIN は NESTED / HASH / MERGE の別
  5. SORT (Disk) / WINDOW (Buffer)
  6. RowsとCostの見積りが現実と乖離していないか(統計の鮮度を疑う)

ロックと待機事象

同時実行制御のためにDBは様々なロックを取りますが、設計を誤ると待機やデッドロックの原因になります。

デッドロック対策の鉄則: 「常に同じ順序でロックを取る」。テーブルの更新順序をコーディング規約で決める。

よくある落とし穴

  • コミットを忘れる: 長時間トランザクションがUNDO/ロックを圧迫し、他セッションを巻き込む。
  • 1行ずつ処理(Loop): Round Tripが爆発。SET演算で一発処理を検討。
  • 全件取得 → アプリ側filter: ネットワーク帯域・メモリを浪費。WHEREはDBに任せる。
  • バインド変数を使わない: 毎回ハードパース。Shared Pool競合・実行計画ぶれの原因。
  • SELECT *: 不要列の転送・INDEX Only Scanの機会損失。
  • TRUNCATEとDELETE混同: TRUNCATEはDDL ⇒ コミット不可・トリガ非発火。
  • NULL比較に「=」を使う: = NULL は常に偽。IS NULL を使う。
  • JOIN結果の重複: 意図しない多対多が発生。DISTINCTで隠さず原因を直す。

まとめ

シリーズ全体のまとめです。

  1. DBは「メモリで処理し、ログで永続化する」
  2. Optimizerは統計を元にコスト最小の計画を選ぶ
  3. INDEXは読み取りを高速化し、書込コストを払う
  4. REDOがコミットの本体。DBの永続性はここに依存
  5. Temporary Spaceは「メモリで足りない時の溢れ先」
  6. DB Linkは便利だが、ネット越し・分散COMMITの罠あり
  7. 設定は「メモリ・ファイル配置・ログ多重化・監査」
実行計画を読める開発者は、DBの中で何が起きているかを語れるようになる。

参考リソース

  • 公式ドキュメント: Oracle Database Concepts / PostgreSQL Documentation / SQL Server Architecture / IBM Db2 Knowledge Center
  • 古典の名著: 「データベース実践入門」「SQLパフォーマンス詳解」「Use The Index, Luke!」
  • 実行計画の読み方: EXPLAIN PLAN (Oracle) / EXPLAIN ANALYZE (Postgres) / SET STATISTICS PROFILE (SQL Server)
  • パフォーマンス分析: AWR / Statspack (Oracle) / pg_stat_statements (PG) / Query Store (SQL Server)