PostgreSQL | SQLite+MySQL経験者向け、30日で習得するPostgreSQL:設計とパフォーマンス - Day16 実行計画

SQL 30日で身につけるPostgreSQL
スポンサーリンク
スポンサーリンク

Day16 前半のゴール

「“SQLがどう動いているか”を目で見て理解する感覚を持つ」

ここからは「設計とパフォーマンス」のフェーズです。
Day16 のテーマは、PostgreSQL の実行計画を見るための EXPLAIN / EXPLAIN ANALYZE。

前半のゴールはこうです。
なぜ実行計画を見る必要があるのかを、感覚レベルで理解する。
EXPLAIN と EXPLAIN ANALYZE の違いを説明できる。
実行計画の“最低限ここだけは見る”ポイント(コスト・行数・インデックス利用)を掴む。

「SQLが遅い」ときに、闇雲に書き換えるのではなく、
“何がボトルネックか”を目で見て判断できるようになるのが、このDayの狙いです。


なぜ実行計画を見るのか

「“遅いSQL”の原因は、コードではなく“動き方”にあることが多い」

まず、根本の話からいきます。

SQLは「何をしたいか」を宣言する言語で、
「どうやって実行するか」は、DB側(PostgreSQL)が決めます。

同じSQLでも、

インデックスを使ってサクッと取る場合
全件をなめてから絞り込む場合

など、内部の“動き方”がまったく違うことがあります。

アプリ側から見ると「同じSQL」でも、
PostgreSQLの中では「全然違うルートで処理している」わけです。

だから、「SQLが遅い」ときに、

WHERE句をちょっといじる
JOINの順番を変える

といった表面的な修正だけをしても、
本質的なボトルネックに届かないことが多い。

そこで必要になるのが、「実行計画を見る」という行為です。


EXPLAIN の基本

「“このSQLをどう実行するつもりか”を教えてもらう」

EXPLAIN は、「このSQLを実行するとき、PostgreSQLはどういう手順で動くつもりか」を教えてくれるコマンドです。

例えば、こういうクエリがあるとします。

SELECT *
FROM order_items
WHERE user_id = 123
ORDER BY ordered_at DESC
LIMIT 10;
SQL

これに対して、こう書きます。

EXPLAIN
SELECT *
FROM order_items
WHERE user_id = 123
ORDER BY ordered_at DESC
LIMIT 10;
SQL

すると、PostgreSQLは「実行計画」をテキストで返してきます。
中身はこんな感じのイメージです(実際の表示は環境によって多少違います)。

Limit  (cost=0.43..15.21 rows=10 width=...)
  ->  Index Scan using idx_order_items_user_id_ordered_at
      on order_items  (cost=0.43..1200.00 rows=8000 width=...)
      Index Cond: (user_id = 123)
      Order By: ordered_at DESC

ここで重要なのは、「この時点では実際にはクエリを実行していない」ということです。
EXPLAIN は、「こう動く予定だよ」という“予告”だけを見せてくれます。


EXPLAIN ANALYZE の基本

「“実際に動かして、その結果も見せて”もらう」

EXPLAIN ANALYZE は、EXPLAIN の“本気版”です。

EXPLAIN ANALYZE
SELECT *
FROM order_items
WHERE user_id = 123
ORDER BY ordered_at DESC
LIMIT 10;
SQL

これは、実際にクエリを実行したうえで、

どのルートで処理したか(実行計画)
どれくらい時間がかかったか
何行を読み、何行を返したか

といった“実測値”を教えてくれます。

表示イメージはこうです。

Limit  (cost=0.43..15.21 rows=10 width=...)
       (actual time=0.050..0.120 rows=10 loops=1)
  ->  Index Scan using idx_order_items_user_id_ordered_at
      on order_items  (cost=0.43..1200.00 rows=8000 width=...)
       (actual time=0.050..0.110 rows=50 loops=1)
      Index Cond: (user_id = 123)
      Order By: ordered_at DESC

ここでのポイントは、

cost=... は「PostgreSQLが事前に予測したコスト」。
actual time=... rows=... は「実際に動かしてみた結果」。

という違いです。

EXPLAIN だけだと「予測」しか分からない。
EXPLAIN ANALYZE だと「予測と現実のズレ」まで分かる。

パフォーマンスチューニングを真面目にやるときは、
基本的に EXPLAIN ANALYZE を使います。


実行計画の“最低限ここだけは見る”3ポイント

「ノイズを捨てて、ボトルネック候補だけを拾う」

実行計画は情報量が多いので、
最初から全部理解しようとすると挫折します。

まずは、次の3つだけに絞って見てください。

どのノードが一番コストが高いか
どのノードが一番時間を食っているか(actual time)
インデックスを使っているか、それとも Seq Scan(全件走査)になっているか

さっきの例で言うと、

Index Scan using idx_order_items_user_id_ordered_at
→ インデックスを使っているので、良さそう。

もしこれが、

Seq Scan on order_items  (cost=...)
  Filter: (user_id = 123)

のようになっていたら、「全件走査してから user_id で絞っている」ことになります。
データ量が多いと、これが一気にボトルネックになります。

「Seq Scan なのか Index Scan なのか」を見るだけでも、
インデックス設計の問題に気づけるようになります。


例題:インデックスが効いている場合と効いていない場合

「実行計画で“インデックス設計の良し悪し”を目で見る」

同じクエリでも、インデックスの有無で実行計画が変わります。

例えば、order_items(user_id, ordered_at) にインデックスがある場合。

CREATE INDEX idx_order_items_user_id_ordered_at
ON order_items (user_id, ordered_at DESC);
SQL

この状態で EXPLAIN すると、さっきのように Index Scan が出ます。

逆に、このインデックスがない状態で EXPLAIN すると、こうなりがちです。

Limit  (cost=0.00..200.00 rows=10 width=...)
  ->  Seq Scan on order_items  (cost=0.00..20000.00 rows=10000 width=...)
      Filter: (user_id = 123)

ここでの違いは、

Index Scan
→ 条件に合う行だけをインデックスから拾ってくる。

Seq Scan
→ テーブル全件をなめて、条件に合う行だけを残す。

というところです。

実行計画を見ることで、

「このクエリはインデックスをちゃんと使っているか?」
「どのカラムにインデックスを張るべきか?」

を、感覚ではなく“証拠付き”で判断できるようになります。


実行計画を見るときの“心構え”

「怖がらずに、まずは1本のクエリから」

正直に言うと、実行計画は最初はかなり怖く見えます。
でも、Day16 前半でやりたいのは、「怖さを少し剥がすこと」です。

やることはシンプルで、

よく使う1本のSELECTを決める。
それに対して EXPLAIN と EXPLAIN ANALYZE を1回ずつ打ってみる。
「Seq Scan か Index Scan か」「どこが一番時間を食っているか」だけを見る。

これだけで、「SQLがどう動いているか」を“目で見る”経験が1つ積めます。

そこから少しずつ、「JOINの実行計画」「GROUP BY の実行計画」などに広げていけばいい。
いきなり全部分かろうとしなくていいので、「まずは1本のクエリと仲良くなる」と決めてしまうのがコツです。


Day16 前半のまとめ

実行計画は、「同じSQLでも、PostgreSQLが内部でどんなルートで処理しているか」を教えてくれるもので、EXPLAIN は「予測だけ」、EXPLAIN ANALYZE は「実際に動かしてみた結果(actual time・rows)」まで見せてくれる。
最低限見るべきポイントは、「Seq Scan なのか Index Scan なのか」「どのノードのコスト・実測時間が一番大きいか」で、インデックスが効いているクエリなら Index Scan using ... が出る一方、効いていないクエリは Seq Scan on ... Filter: ... のように全件走査になっていることが多い。
Day16 前半の着地点は、「よく使う1本のクエリに対して EXPLAIN / EXPLAIN ANALYZE を打ち、SQLの“動き方”を目で見てからインデックスや設計を考える」という習慣の入口に立つこと。

タイトルとURLをコピーしました