Skip to content

PostgreSQLのEXPLAINを読む

This content is a draft and will not be included in production builds.

https://taityo-diary.hatenablog.jp/entry/2018/07/07/071928

https://speakerdeck.com/keiko713/explain-explain

id:shiba_yu36 と眺めたメモ

まずはどのクエリの何が問題なのかを調べる必要がある。これは、KibanaやNewRelicなどから読み取れることが多い気がする。この時、プレースホルダが使われている場合は、その値をなるべく同じように再構成した方が良い。

データベースでクエリを評価する

Section titled “データベースでクエリを評価する”

まずはpsqlコマンドなどでDBに接続する。接続するデータベースのホスト名が分からない場合は、Mackerelのdbロールを探して、ホストのメタデータを見るとEndpointに書いてある。

Terminal window
psql -U nobody --password -h $host mackerel3

または、更新系のクエリを実行したい場合は、スナップショットからインスタンスを作ってそちらに接続する。

psqlコマンドなどでDBに繋いでクエリを実行する。この時に、EXPLAIN句をつけて実行する。

EXPLAIN (ANALYZE,BUFERS) SELECT * FROM xxx;

EXPLAIN のオプションには色々あるけど、おそらくこの辺りをよく使う。

おそらくこのような結果になるはず。(長いので一部省略)

Terminal window
mackerel3=> explain (analyze,buffers) select id, role_id, name, deleted, created_at, updated_at from role_metrics where role_id in (889771156637323264) and name in ('cpu.user.percentage', 'memory.cached');
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on role_metrics (cost=44.35..91.15 rows=12 width=54) (actual time=0.033..0.035 rows=9 loops=1)
Recheck Cond: ((role_id = '889771156637323264'::bigint) AND ((name)::text = ANY ('{cpu.user.percentage,memory.cached}'::text[])))
Heap Blocks: exact=1
Buffers: shared hit=31
-> Bitmap Index Scan on role_metrics_idx_role_id_name (cost=0.00..44.35 rows=12 width=0) (actual time=0.027..0.027 rows=9 loops=1)
Index Cond: ((role_id = '889771156637323264'::bigint) AND ((name)::text = ANY ('{cpu.user.percentage,memory.cached}'::text[])))
Buffers: shared hit=30
Planning time: 0.177 ms
Execution time: 0.065 ms
(9 rows)

この結果から

  • プラン演算子(Seq Scan, Index Scan, Bitmap Index Scanなど)
  • 取得した行数
  • キャッシュヒット数

などがわかる。この資料が詳しい。

cost= の値は計画時点のコスト、actual time=が実際の実行結果を表す。actual timeはANALYZEオプションを追加しないと出力されない。

exactモードとlossyモードがある。Bitmap Index Scanのビットマップサイズがメモリから溢れるとlossyモードに切り替わるらしい。

loopsは副計画ノードを実行した回数。actual timeは1回あたりの時間なので、これが1以上の場合は乗算して実コストを計算する。らしい。widthはよく分からなかった。

(cost=44.35..91.15 rows=12 width=54) (actual time=0.033..0.035 rows=9 loops=1)

costに比べてactual timeが小さすぎるけど、これはPostgreSQLの内部を知らないと無理そう。