postgresqlクエリ実行の基礎知識 ~explainを読み解こう~

88
PostgreSQLクエリ実行の基礎知識 Explainを読み解こう~ 30(仮名)PostgreSQL勉強会 20141011TIS株式会社 下雅意美紀 1

Upload: miki-shimogai

Post on 03-Jul-2015

5.793 views

Category:

Technology


4 download

DESCRIPTION

2014年10月11日 JPUGの発表資料

TRANSCRIPT

Page 1: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

PostgreSQLクエリ実行の基礎知識~Explainを読み解こう~

第30回 (仮名)PostgreSQL勉強会

2014年10月11日

TIS株式会社 下雅意美紀

1

Page 2: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

自己紹介

• 氏名 下雅意美紀

• 所属 TIS株式会社

• 経歴 入社1年目

• PostgreSQL歴 = 入社歴

• 業務で勉強する以外にも、前回のJPUGのしくみ分科会にも参加したり(http://thinkit.co.jp/story/2014/07/01/5074)、PGEConsにも参加したりとコミュニティ活動なども通して日々PostgreSQLの勉強をしています。

2

Page 3: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

PostgreSQLクエリ実行の基礎知識~Explainを読み解こう~

アジェンダ

・PostgreSQLのクエリ実行の概要

・Explain実行結果(問い合わせプラン)の読み方

・Explain演算子の種類

・問い合わせプランを変更させる

・実際のデバック例3

目標クエリチューニングで使用するExlpainコマンドが出力する実行計画を読めるようになりましょう。PostgreSQLがクエリ実行時に内部でどのような処理を行っているかを知りましょう。

Page 4: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

クエリの実行

そもそもクエリはどのようにPostgreSQL内部で実行されているのか

4

Page 5: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

PostgreSQLのクエリ処理のフロー

DBにテーブルがあるか確認

クエリの書き換え

プランを実行

5

Page 6: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

パーサ• クエリの構文を解析して、文字列が正しい構文になっているかをチェック

• 構文が正しくなければエラーを返す

• 構文が正しければパースツリーの形に変換

• テーブル名やカラム名が実在するかは問わないので「ローパースツリー」とも呼ばれる

SELECT oid FROM pg_proc WHERE oid = 1;

SELECT

TargetList

oid

RelationList

pg_proc

Qualifier

Expression

=

1oid 6

Page 7: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

PostgreSQLのクエリ処理のフロー

クエリの書き換え

プランを実行

7

DBにテーブルがあるか確認

Page 8: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

アナライザ• パースツリーをもとにどのような表や関数、演算子が参照されるかを判断して、クエリツリーを作成する

• この時DBにテーブルが存在するかチェックし、存在したらテーブル名をOIDに変換する

SELECT oid FROM pg_proc WHERE oid = 1;

SELECT

TargetList

oid

RelationList

pg_proc

Qualifier

Expression

=

1oid

カタログ

oidに変更 8

Page 9: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

PostgreSQLのクエリ処理のフロー

DBにテーブルがあるか確認

クエリの書き換え

プランを実行

9

Page 10: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

リライタ

• PostgreSQLではVIEWやRULEはクエリを書き換えることで実行している

• このような書き換えの処理をリライタという

10

Page 11: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

PostgreSQLのクエリ処理のフロー

DBにテーブルがあるか確認

クエリの書き換え

プランを実行

11

Page 12: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

プランナ• クエリツリーを解析して、実際に問い合わせを実行するための問い合わせプランを求める

• 問い合わせの実行コストを見積もり、最小コストのものを問い合わせプランとする(問い合わせの最適化)

• コスト計算のための最小単位はパス

• パスは各処理によって種類がある

• ここで作成された問い合わせプランはあくまで推定されたもの

クエリツリー

パス単位でコスト計算

最小コストのものを問い合わせプランに

パスの種類・全件スキャン・インデックスを使ってスキャン・テーブル結合など

12

Page 13: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

問い合わせプランとは・問い合わせプランとは、問い合わせを実行するときに、

・どの方式でテーブルを検索するか

・複数のテーブルがある時はどの順序や結合方式で検索するか

などを記述したもの

・問い合わせプランの優劣はコストで決まる

・Explainはこの問い合わせプランを表示するコマンド

13

Page 14: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

PostgreSQLのクエリ処理のフロー

DBにテーブルがあるか確認

クエリの書き換え

プランを実行

14

Page 15: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

エグゼキュータ

• プランナで作成した問い合わせプランを実際に実行する

• 方式によって実行ルーチンが変わる

表スキャン

結合処理

条件処理

15

Page 16: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Explainとは• PostgreSQLはクエリ(SQL文)を実行する際にクエリを分解して、最も効率の良い問い合わせプラン(実行計画)でデータを取得してくる

• 問い合わせプランはPostgreSQLが推定してくれる

• そのPostgreSQLによって推定された問い合わせプランの詳細

を表示してくれるのがExplainです。

16

Explain

クエリ実行のための問い合わせプラン

Page 17: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

なぜExplainしなければならないのか• クエリをより高速に実行しDBのパフォーマンスを上げるには

(クエリチューニングするには)、クエリの構造とクエリに含まれるデータの性質にとって最適なプランを持つことが重要

• しかしその最適なプランとPostgreSQLによって推定されたプランがいつも一致するとは限らない

• PostgreSQLによって推定されたプランが最適なプランかどうか確認するためにもExplainで確認することは大切

17

最適なプラン推定されたプラン =

Explainコマンドで確認しよう!

Page 18: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Explainの実行結果の読み方

18

Page 19: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Explain Planの例

=# EXPLAIN SELECT * FROM pg_procORDER BY proname;

QUERY PLAN----------------------------------------------------Sort (cost=181.55..185.92 rows=1747 width=322)

Sort Key: proname-> Seq Scan on pg_proc

(cost=0.00..87.47 rows=1747 width=322)

19

Page 20: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

20

Explaining → Cost

• コストは、プランナがさまざまなプランの中からある特定のプランを選ぶための指標である

• 2つのコスト: 初期コスト (左) とトータルコスト (右)– 実行プランの比較で重要なのはトータルコスト。– いくつかの演算子には初期コストがある。無いものもある。– コストは推定値に過ぎない。その算出は結構複雑。

• 値はシーケンシャルI/Oで1ページを読み込むコストを 1.0 とした際の相対値で示される。

=# EXPLAIN SELECT oid FROM pg_proc;QUERY PLAN

------------------------------------------Seq Scan on pg_proc

(cost=0.00..87.47 rows=1747 width=4)

Page 21: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

初期コストとトータルコスト

21

初期コスト 実行コスト最後の行を返す

最初の行を返す

cost=0.00..87.47

トータルコスト

Page 22: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Explaining → Cost

パラメータ 説明 規定値 相対速度

seq_page_cost シーケンシャル読み込み1回 1.00 (基準)

random_page_cost ランダム読み込み1回 4.00 4倍遅い

cpu_tuple_cost 行の処理1回 0.01 100倍速い

cpu_index_tuple_cost 索引の処理1回 0.005 200倍速い

cpu_operator_cost 計算1回 0.0025 400倍速い

effective_cache_size ページキャッシュサイズ 128MB N/A

22

Page 23: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

23

Explaining → Rows

• 推定された行数を表示する

• PostgreSQL 8.0以前では、一度もVACUUM/ANALYZEされていないテーブルについては1000行がデフォルト。

• 実際の値と大きくかけ離れている場合、vacuum あるいはanalyzeをすべきというサインである。

=# EXPLAIN SELECT oid FROM pg_proc;QUERY PLAN

------------------------------------------Seq Scan on pg_proc

(cost=0.00..87.47 rows=1747 width=4)

Page 24: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

24

Explaining → Widths

• このレベルにおける推定された入力サイズを表示する。

• それほど重要ではない

=# EXPLAIN SELECT oid FROM pg_proc;QUERY PLAN

------------------------------------------Seq Scan on pg_proc

(cost=0.00..87.47 rows=1747 width=4)

smallint 2

integer 4

bigint 8

boolean 1

char(n) n+1

n+4

varchar(n)

text [ n文字 ]

一般的なデータ型のサイズについて

Page 25: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

しかし、これらはみな推定された値である。

→実際のコストや行数を知るには?

25

Page 26: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

26

Explaining → Explain Analyze

• 実際にクエリを実行し、実際の情報を表示する。• 時間はミリ秒で表示される。「コスト」とは無関係。• 全体の実行時間も表示される。• 「loops」は処理の繰り返し回数。実行時間(time)は繰り返し全体の時間を表す。

=# EXPLAIN ANALYZE SELECT oid FROM pg_proc;QUERY PLAN

------------------------------------------Seq Scan on pg_proc

(cost=0.00..87.47 rows=1747 width=4)(actual time=0.077..17.082 rows=1747 loops=1)

Total runtime: 20.125 ms

Page 27: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Explainの演算子

27

Page 28: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Explain演算子とは

• クエリを実行するための内部的な処理の計算タイプ

• 処理の内容に応じていくつかの種類がある

• PostgreSQLのプランナは、統計情報やwork_memの

大きさなどをもとに、複数の演算子を組み合わせ、最も最適と推定される問い合わせプランを選択する

• この演算子を変更して問い合わせプランを変更するのがクエリチューニング

→演算子の処理について知っておくことが大切

28

Page 29: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

主な演算子一覧分類 演算子 処理

テーブルスキャン Seq scan インデックスを使用せず、全件を検索

Index scan インデックスを使用してスキャン

Bitmap scan ビットマップを使用してスキャン

Index only scan 問い合わせがインデックスに含まれるカラムのみで完結する場合のスキャン

Tid scan 検索条件がタプルID(ctid)のスキャン

その他のスキャン Function scan 関数がデータをgatherした結果をスキャン

テーブルの結合 Nested Loop ネステッド・ループ結合を行う

Merge Join ソート・マージ結合を行う

Hash Join ハッシュ結合を行う

29

Page 30: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

分類 演算子 処理

検索結果に対して作用

Group GROUP BY

limit LIMIT,OFFSET

Unique UNIQUE

Aggregate 集計関数(count,sum,,,)

GroupAggregate

集計関数にGROUP BYを使用し、より大きな結果のセットを得る

Result 非テーブル問い合わせ

結果の結合 Append UNION(和)

SetOp INTERSECT(積),EXCEPT(差)

その他の処理を補助

Sort ソート処理

30更に詳しく知りたい方はこちらをどうぞ→

第20回目しくみ分科会 Explaing Explain第2回目http://www.postgresql.jp/wg/shikumi/sikumi_20/

Page 31: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

テーブルスキャンする演算子

31

分類 演算子

テーブルスキャン Seq Scan

Index Scan

Bitmap Scan

Index Only Scan

Tid Scan

その他スキャン Function Scan

テーブルの結合 Nested Loop

Merge Join

Hash Join

分類 演算子

検索結果に対して作用

Group

limit

Unique

Aggregate

Group Aggregate

Result

結果の結合 Append

SetOp

その他の処理を補助

Sort

Page 32: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

32

Seq Scan 演算子

• 最も基本。単に表を最初から最後へとスキャンする

• 条件にかかわらず各行をチェックする

• 大きなテーブルはインデックススキャンの方が良い

• コスト(開始コスト無し), 行(タプル), 幅(oid)

• トータルコストは92.12

=# EXPLAIN SELECT oid FROM pg_proc WHERE oid = 1;QUERY PLAN

--------------------------------------------------Seq Scan on pg_proc(cost=0.00..92.12 rows=1 width=4)Filter: (oid = 1::oid)

Page 33: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

33

Seq Scan について• テーブルを最初から最後までチェックして必要な行を探す。

• 検索条件に合致するインデックスがない場合はこれしかない。

• インデックスが使えても対象行が多い場合は Seq Scan に。

⇒プランナがコスト計算した結果を比較して判断する。

id = 1

id = 11

id = 34

id = 45

・・・

テーブルid列のインデックス

使わない

先頭行から順に走査(最後の行まで見る必要あり)

Seq Scan のコスト= DISK I/O コスト + CPU コスト= テーブル全ページ数×sequential_page_cost

+ テーブル全行数×cpu_tuple_cost+ テーブル全行数×cpu_operator_cost

・ テーブル全ページ数 pg_class の relpages・ テーブル全行数 pg_calss の reltuples・ シーケンシャルにDISK1ページを読むコスト

sequential_page_cost = 1・ 1行を走査するCPUコスト

cpu_tuple_cost = 0.01・ 計算1回のCPUコスト (条件絞りこみ)

cpu_operator_cost = 0.0025

Page 34: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Seq Scanのコスト計算Seq Scanのコスト = (DISK I/Oコスト)+(CPUコスト)

=(テーブル全ページ数×Seq_page_cost)

+(テーブル全行数× cpu_tuple_cost )

+(テーブル全行数×cpu_operator_cost)

34

=# SELECT relpages,reltuples FROM pg_classWHERE relname = 'pg_proc';

relpages | reltuples----------+-----------

61 | 2490

=# EXPLAIN SELECT oid FROM pg_proc WHERE oid = 1;QUERY PLAN

--------------------------------------------------Seq Scan on pg_proc(cost=0.00..92.12 rows=1 width=4)Filter: (oid = 1::oid)

総コスト= (61×1.0) + (2490×0.01)

+ (2490×0.0025) = 92.125

テーブル全ページ数

テーブル全行数

規定値 0.0025

Page 35: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

35

Index Scan 演算子

• 特に大きなテーブルではコストが低くなるので選ばれる可能性が高い

• Index Condが無い場合は、ソートの代わりとして使われるインデックス順のフルスキャンを表す

=# EXPLAIN SELECT oid FROM pg_proc WHERE oid=1;QUERY PLAN

-----------------------------------------------------Index Scan using pg_proc_oid_index on pg_proc

(cost=0.00..5.99 rows=1 width=4)Index Cond: (oid = 1::oid)

Page 36: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

36

Index Scan について• 検索条件に合致するインデックスがあれば検討する。

• 通常は対象行が少なければこちらが選択される。

• インデックスとテーブルを交互にアクセスする。

id = 1

id = 11

id = 34

id = 45

・・・

1 11 45 100

テーブルid列のインデックス

検索条件に合うリーフノードを探索

必要な行にランダムアクセス

Index Scan のコスト= インデックスI/Oコスト + テーブルI/Oコスト+ インデックスCPUコスト + テーブルCPUコスト

インデックスI/Oコスト= 必要ページ数×sequential_page_cost(1)

テーブルI/Oコスト= 必要行数×(1~4) ※

インデックスCPUコスト= 必要行数×cpu_index_tuple_cost(0.005)

テーブルCPUコスト= 必要行数×cpu_tuple_cost(0.01)

※アクセスページがメモリサイズ(effective_cache_size)の何倍かでアクセスコストは変化

Page 37: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

37

必要行数って何?

• 指定条件を満たす行数

• Explain の実行結果で 「rows=100」 とか表示される値。

• プランナがテーブルの統計情報から行数を予測。

⇒実際に検索した時の行数とは異なる。

• 必要行数 = 選択度×テーブル行数

• 「選択度」は対象データがカラムに存在する割合

• 選択度は対象カラムの統計情報を使って計算

• 各カラム値の分布は pg_stats(pg_statistic)でわかる。

⇒ ANALYZE時に収集した情報が格納されている。

Page 38: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

38

Bitmap Scan 演算子

• 8.1で追加された

• BitmapOr, BitmapAndで複数のビットマップを合体

• リレーションの”ビットマップ“をメモリ内で作成する

Page 39: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

39

Bitmap Scan について• インデックスを有効に使って検索効率を向上させる機構

• ORで結合した条件式を使った検索に効果大

id1 = 1, id2 = 33

id1 = 11, id2 = 55

id1 = 34, id2 = 77

id1 = 45, id2 = 99

・・・

1 11 34 45

テーブル

id1列のインデックス

ビットONの行を取得

33 55 77 99

id2列のインデックス

WHERE (id1 BETWEEN 10 AND 40) OR (id2 BETWEEN 20 AND 70)

0

1

1

0

・・

1

1

0

0

・・

Bitmap Index Scan

1

1

1

0

・・

BitmapOr

条件を満たす行(TID)をビットマップとして生成

ビットマップ同士でOR演算

Bitmap Heap Scan

同一ページ内に複数の対象行がある場合、まとめて取得できるので、I/Oコストが有利になる。

Page 40: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Index Only Scan

• 9.2で追加された

• 取得したい値にインデックスが含まれるとき、テーブルのアクセスを省略して検索する

• 非常に高速(しかしindex only scanが選ばれるには条件が…)

40

Page 41: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Index Only Scanのスキャン方法

• Index Only ScanはまずインデックスからVisibility Mapを参照しに行く(早速テーブルには行かない)

• そして高速に値を返せるか返せないかは、実はこのVisibility Mapにかかっている

→このVisibility Mapって一体何者なの?

41

Page 42: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Visibility Mapとは• ページ内にトランザクションによって更新され、参照することができなくなったタプルがあるかどうかを、ビットで管理している

• テーブルのブロック毎に1bitのデータ領域を確保し、不要なタプルがない&&どのトランザクションも更新していないブロックには”0”を、それ以外は”1”を保存する

42

ブロック1の可視性

ブロック2の可視性

ブロック1

ブロック2

ブロック3

ブロック4

Page 43: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Visibility Mapのbitが0だと• テーブルにアクセスすることなくタプルの値を返す

43

ブロック

1

2

3

4

全タプル有効!

1 11 34 45

インデックス

SELECT id FROM table1 WHERE id BETWEEN 1 AND 11

テーブル

テーブルにアクセスしない

Page 44: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Visibility Mapのbitが1だと• タプルの値が返せるものか判断するために通常のテーブルアクセスを行う

44

無効タプルあり!

ブロック

1

2

3

41 11 34 45

インデックス

SELECT id FROM table1 WHERE id BETWEEN 1 AND 11

テーブル

1

本当に値返していいの?テーブルにアクセスしよう

Page 45: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

不要タプルの回収・トランザクションによって発生した不要タプルを回収するにはvacuumを行う

無効タプル

45

ブロック2ViisibilityMap

ブロック2

ブロック1

ブロック3

ブロック4

ブロック5

テーブルVACUUM前

VACUUM後

再利用可能タプル

ブロック2ViisibilityMap

ブロック2

ブロック1

ブロック3

ブロック4

ブロック5

テーブル

Page 46: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

46

Tid Scan 演算子

• カラムタプルID• “ctid=”がクエリに指定された場合のみ使われる• 滅多に使わない、非常に速い

=# EXPLAIN SELECT oid FROM pg_proc WHERE ctid = '(0,1)';QUERY PLAN

------------------------------------------------------Tid Scan on pg_proc (cost=0.00..4.01 rows=1 width=4)Filter: (ctid = '(0,1)'::tid)

Page 47: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

処理を補助する演算子

47

分類 演算子

テーブルスキャン Seq Scan

Index Scan

Bitmap Scan

Index Only Scan

Tid Scan

その他スキャン Function Scan

テーブルの結合 Nested Loop

Merge Join

Hash Join

分類 演算子

検索結果に対して作用

Group

limit

Unique

Aggregate

Group Aggregate

Result

結果の結合 Append

SetOp

その他の処理を補助

Sort

Page 48: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

48

Sort 演算子

• 明示的なソート : ORDER BY句

• 暗黙的なソート : Unique, Sort-Merge Join など

• 開始コストを持っている: 最初の値はすぐには返却されない

=# EXPLAIN SELECT oid FROM pg_proc ORDER BY oid;QUERY PLAN

---------------------------------------------Sort (cost=181.55..185.92 rows=1747 width=4)

Sort Key: oid-> Seq Scan on pg_proc

(cost=0.00..87.47 rows=1747 width=4)

Page 49: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

49

Sort について• データが作業メモリ(work_mem)に収まればクイックソート

– DISK I/Oが発生しない。高速。

• 作業メモリに収まらなければ外部ソートを選択

– アルゴリズムは「マージソート」

– DISK I/Oが発生するのでクイックソートより低速。

– I/Oコストは対象データと work_memのサイズで変わってくる。

work_memに収まるサイズ毎にソートを繰り返すから。多分。。。

– 詳しくは /src/backend/optimizer/path/costsize.cの cost_sort参照。

7

4

9

2

DISK

•対象データを全てメモリに保持

• メモリ上でクイックソートを実行

7

4

9

2

2

4

7

9

2

4

7

9

8

1

3

5

1

3

5

8

DISK DISK

・・・

【対象データ < work_mem】

work_mem

【対象データ > work_mem】

work_mem 以下を繰り返す。•データの一部をメモリに保持

• メモリ上でソート実行•ソート結果を

DISKに戻す

Page 50: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

50

Sortの実行例# EXPLAIN ANALYZE SELECT * FROM pgbench_accounts ORDER BY bid;--------------------------------------------------------------------------------------------------------------------------Sort (cost=290114.34..292614.34 rows=1000000 width=97)

(actual time=882.522..1186.626 rows=1000000 loops=1)Sort Key: bidSort Method: external sort Disk: 104600kB-> Seq Scan on pgbench_accounts (cost=0.00..26394.00 rows=1000000 width=97)

(actual time=0.023..176.540 rows=1000000 loops=1)Total runtime: 1264.377 ms

LOG: temporary file: path "base/pgsql_tmp/pgsql_tmp32001.0", size 107110400STATEMENT: EXPLAIN ANALYZE SELECT * FROM pgbench_accounts ORDER BY bid;

※ログに一時ファイル作成状況を表示(postgresql.conf の log_temp_files = 0)

メモリに収まったのでクイックソート

ソート対象が大きいので外部ソート

# SET work_mem=‘200MB’;# explain analyze select * from pgbench_accounts order by bid;--------------------------------------------------------------------------------------------------------------------------Sort (cost=126051.84..128551.84 rows=1000000 width=97)

(actual time=472.334..563.570 rows=1000000 loops=1)Sort Key: bidSort Method: quicksort Memory: 165202kB-> Seq Scan on pgbench_accounts (cost=0.00..26394.00 rows=1000000 width=97)

(actual time=0.030..166.788 rows=1000000 loops=1)Total runtime: 634.155 ms

※ work_mem を1⇒200MBに拡張して実行してみる

Page 51: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

テーブル結合する演算子

51

分類 演算子

テーブルスキャン Seq Scan

Index Scan

Bitmap Scan

Index Only Scan

Tid Scan

その他スキャン Function Scan

テーブルの結合 Nested Loop

Merge Join

Hash Join

分類 演算子

検索結果に対して作用

Group

limit

Unique

Aggregate

Group Aggregate

Result

結果の結合 Append

SetOp

その他の処理を補助

Sort

Page 52: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

52

Nested Loop 演算子

• 2つのテーブルの結合(2つの入力セット)• INNER JOIN と LEFT OUTER JOIN の使用• 「外部」テーブルをスキャンし、「内部」テーブルにマッチするものの発見• 開始コスト無し• インデックスが無い場合遅い問い合わせになる可能性、特にselect句に関数がある場合

=# EXPLAIN SELECT * FROM pgbench_accounts AS a,

pgbench_accounts AS b;

QUERY PLAN

---------------------------------------------------------------

Nested Loop

(cost=0.00..27637054248.00 rows=1000000000000 width=194)

-> Seq Scan on pgbench_accounts a

(cost=0.00..25874.00 rows=1000000 width=97)

-> Materialize (cost=0.00..46011.00 rows=1000000 width=97)

-> Seq Scan on pgbench_accounts b (cost=0.00..25874.00 rows=1000000 width=97)

Page 53: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Nested loop

・もっとも単純な結合方式

・外部テーブルを一レコードずつ取り出し、その都度内部テーブルの全レコードとマッチングする

・初期コストは0、総コストは結合するテーブルのレコード数の積に比する(O(M×N))

53

Page 54: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Merge Join 演算子# EXPLAIN SELECT * FROM pgbench_accounts AS a,

pgbench_tellers AS t where a.aid = t.tid;

QUERY PLAN

---------------------------------------------------------------

Merge Join (cost=5.94..11.25 rows=100 width=449)

Merge Cond: (a.aid = t.tid)

-> Index Scan using pgbench_accounts_pkey on pgbench_accounts a

(cost=0.42..39669.43 rows=1000000 width=97)

-> Sort (cost=5.32..5.57 rows=100 width=352)

Sort Key: t.tid

-> Seq Scan on pgbench_tellers t

(cost=0.00..2.00 rows=100 width=352)

• 二つのデータセットをJOINする:outerとinner• Merge Right JoinとMerge In Joinがある

• データセットはあらかじめソートされていなければならず、また両方同時に走査される。

54

Page 55: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

55

Merge Join (ソートマージ)

• 事前に両方のテーブルを結合キーでソートする。

• 両方のテーブルを先頭からマッチングしていく。

⇒テーブルを1回調べればよく、テーブルの走査回数減

• 処理対象の行が多いケースで有効

2

4

7

9

2

7

4

9

2

1

4

3

外側テーブル(N件)

内側テーブル(M件)

結合キーで事前にソート

両テーブルをキー順にマッチング

1

2

3

4

結合キーで事前にソート

•ソートさえできれば速いが。。。

⇒インデックスがない列が結合キー

の場合はコスト大。

•計算量は O(NlogN + MlogM)。

Page 56: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

56

Hash & Hash Join 演算子

• Hashは、異なる Hash Join演算子で使用されるハッシュテーブルを作成する

• 一方の入力からハッシュテーブルを作成し、二つの入力を比較する• INNER JOIN、OUTER JOINと同時に使われる• ハッシュの作成にはスタートアップコストが伴う

=# EXPLAIN SELECT * FROM pgbench_accounts AS a,

pgbench_tellers AS t where a.bid = t.bid;

QUERY PLAN

---------------------------------------------------------------

Hash Join (cost=3.25..140877.25 rows=10000000 width=449)

Hash Cond: (a.bid = t.bid)

-> Seq Scan on pgbench_accounts a

(cost=0.00..25874.00 rows=1000000 width=97)

-> Hash (cost=2.00..2.00 rows=100 width=352)

-> Seq Scan on pgbench_tellers t

(cost=0.00..2.00 rows=100 width=352)

Page 57: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

57

Hash Join(ハッシュ値マッチング)

• 事前に内側テーブルのハッシュ表を作成。

⇒ハッシュ表を作成する初期コストが必要。• 外側テーブルとハッシュ表を突き合わせる。• ハッシュ表がメモリ(work_mem)に収まらないと性能劣化。

2

7

4

9

2

1

4

3

•一度ハッシュ表を作ってしまえば、

メモリ内で検索を行えるので

ハッシュ表の検索は高速。

•計算量のオーダーは O(N+M)。

外側テーブル(N件)

内側テーブル(M件)

ハッシュ表を事前に作成

ハッシュ表(メモリ内)

先頭から1行ずつスキャン

結合キーのハッシュ値でハッシュ表を検索

Page 58: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

JOINの比較

• Nested loop joinは,データが小さい場合に向いている

• Merge joinは,データ量が多い場合に向いている

• Hash joinは、ソートメモリーが十分にある場合に向いている

58

Page 59: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

検索結果に対して作用する演算子

59

分類 演算子

テーブルスキャン Seq Scan

Index Scan

Bitmap Scan

Index Only Scan

Tid Scan

その他スキャン Function Scan

テーブルの結合 Nested Loop

Merge Join

Hash Join

分類 演算子

検索結果に対して作用

Group

limit

Unique

Aggregate

Group Aggregate

Result

結果の結合 Append

SetOp

その他の処理を補助

Sort

Page 60: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

60

Limit 演算子

• 行は指定された数に等しい• 最初の行を即時に返す• 少量の開始コスト追加でオフセットの扱いも可

=# EXPLAIN SELECT oid FROM pg_proc LIMIT 5;QUERY PLAN

------------------------------------------Limit (cost=0.00..0.25 rows=5 width=4)-> Seq Scan on pg_proc

(cost=0.00..87.47 rows=1747 width=4)

=# EXPLAIN SELECT oid FROM pg_proc LIMIT 5 OFFSET 5;QUERY PLAN

------------------------------------------Limit (cost=0.25..0.50 rows=5 width=4)-> Seq Scan on pg_proc

(cost=0.00..87.47 rows=1747 width=4)

Page 61: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

61

Result 演算子

• 非テーブル問い合わせ

• テーブルを参照せずに結果が得られる場合

=# EXPLAIN SELECT 1 + 1 ;QUERY PLAN

------------------------------------------Result (cost=0.00..0.01 rows=1 width=0)

Page 62: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

結果を結合する演算子

62

分類 演算子

テーブルスキャン Seq Scan

Index Scan

Bitmap Scan

Index Only Scan

Tid Scan

その他スキャン Function Scan

テーブルの結合 Nested Loop

Merge Join

Hash Join

分類 演算子

検索結果に対して作用

Group

limit

Unique

Aggregate

Group Aggregate

Result

結果の結合 Append

SetOp

その他の処理を補助

Sort

Page 63: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

63

Append 演算子

• UNION (ALL) によるトリガー, 継承• 開始コスト無し• コストは単に全ての入力の合計

=# EXPLAIN SELECT oid FROM pg_procUNION ALL SELECT oid ORDER BY pg_proc;

QUERY PLAN--------------------------------------------------------------Append (cost=0.00..209.88 rows=3494 width=4)-> Seq Scan on pg_proc (cost=0.00..87.47 rows=1747 width=4)-> Seq Scan on pg_proc (cost=0.00..87.47 rows=1747 width=4)

Page 64: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

64

SetOp 演算子

• INTERSECT, INTERSECT ALL, EXCEPT, EXCEPT ALL句のために使用される– SetOp Intersect, Intersect All, Except, Except All

=# EXPLAIN SELECT oid FROM pg_proc INTERSECT SELECT oid FROM pg_proc;QUERY PLAN

------------------------------------------------------------------------SetOp Intersect (cost=415.51..432.98 rows=349 width=4)-> Sort (cost=415.51..424.25 rows=3494 width=4)

Sort Key: oid-> Append (cost=0.00..209.88 rows=3494 width=4)

-> Subquery Scan "*SELECT* 1" (cost=0.00..104.94 rows=1747)-> Seq Scan on pg_proc (cost=0.00..87.47 rows=1747)

-> Subquery Scan "*SELECT* 2" (cost=0.00..104.94 rows=1747)-> Seq Scan on pg_proc (cost=0.00..87.47 rows=1747)

Page 65: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

実行プランを変える

65

Page 66: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

その前に• どの問い合わせプランを選んでくれるかは基本的にはPostgreSQL任せ

• プランナーは人より賢いのでむやみに強制しないほうが良い

• PostgreSQLコミュニティの考えも以下のよう• We are not interested in implementing hints in the exact ways they are commonly

implemented on other databases. Proposals based on "because they've got them" will not be welcomed. If you have an idea that avoids the problems that have been observed with other hint systems, that could lead to valuable discussion.

• →他のDBにあるからなんて理由でPostgreSQLにヒント機能を持たせるのは歓迎し

ません。もし既存のヒント機能における問題点を避けれるようなアイデアがあるなら、そこで初めて議論しましょう。

• 他方では、プランナーは推測しかしない– 統計情報を正しい状態に保つため定期的なANALYZEを。

66

Page 67: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

実行プランの強制例①

• pg_hint_planを使用する

• pg_hint_planは追加で提供されているモジュール

• PostgreSQLのプランナに対して、どのような問い合わせプランを選ぶべきか指示(HINT)を与えることができる。

• クエリ単位で演算子を強制する

67

Page 68: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

pg_hint_planの実行例

=# EXPLAIN SELECT aid FROM pgbench_accounts ;

QUERY PLAN

---------------------------------------------------------------------- Index Only Scan using pgbench_accounts_pkey on pgbench_accounts(cost=0.00..2384.26 rows=100000 width=4)

=# /*+ SeqScan(pgbench_accounts) */ explain select aid from pgbench_accounts;

QUERY PLAN

---------------------------------------------------------------------- Seq Scan on pgbench_accounts (cost=0.00..2588.00 rows=100000 width=4)

68

Page 69: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

69

実行プランの強制②:• SETコマンドで演算子を無効にする• SET enable_演算子 = off;

– プランナーがある演算子を使おうとするのを「強く思いとどまらせる」ことができる

• Planner Method Configuration (on/off)– enable_bitmapscan– enable_hashagg– enable_hashjoin– enable_indexscan– enable_indexonlyscan– enable_mergejoin– enable_nestloop– enable_seqscan– enable_sort– enable_tidscan

• セッション単位で演算子を強制する

Page 70: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

70

SETコマンドの実行例

=# EXPLAIN ANALYZE SELECT * FROM pg_class WHERE oid > 2112;QUERY PLAN

------------------------------------------------Seq Scan on pg_class

(cost=0.00..7.33 rows=62 width=164)(actual time=0.087..1.700 rows=174 loops=1)

Filter: (oid > 2112::oid)Total runtime: 2.413 ms

=# SET enable_seqscan = off;=# EXPLAIN ANALYZE SELECT * ORDER BY pg_class WHERE oid > 2112;

QUERY PLAN------------------------------------------------Index Scan using pg_class_oid_index on pg_class

(cost=0.00..22.84 rows=62 width=164)(actual time=0.144..1.802 rows=174 loops=1)

Index Cond: (oid > 2112::oid)Total runtime: 2.653 ms

Page 71: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

71

Seq Scan の強制

• 始動コストに 100000000.0 を足すだけ

– /src/backend/optimizer/path/costsize.c

=# EXPLAIN SELECT * FROM pg_class;QUERY PLAN

-------------------------------------------------------Seq Scan on pg_class(cost=100000000.00..100000006.86 rows=186 width=164)

Page 72: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

実際のデバック例VACUUM ANALYZEをしよう

72

Page 73: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

このようなテーブルを用意

73

exception

exception_id

complete

exception_notice_map

exception_notice_map_id

exception_id

notice_id

SELECT exception_id FROM exceptionJOIN exception_notice_mapUSING (exception_id)WHERE complete IS FALSE AND notice_id = 3;

[boolean]

インデックスexception_id

部分インデックスcomplete=FALSE全体の0.25%

Page 74: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

74

VACUUM ANALYZE前

=# EXPLAIN ANALYZE SELECT exception_id FROM exceptionJOIN exception_notice_map USING (exception_id)WHERE complete IS FALSE AND notice_id = 3;

QUERY PLAN--------------------------------------------------------------------Nested Loop (cost=0.00..2654.65 rows=199 width=8)

(actual time=151.16..538.45 rows=124 loops=1)-> Seq Scan on exception_notice_map

(cost=0.00..250.50 rows=399 width=4)(actual time=0.10..101.61 rows=15181 loops=1)

Filter: (notice_id = 3)-> Index Scan using exception_pkey on exception

(cost=0.00..6.01 rows=1 width=4)(actual time=0.03..0.03 rows=0 loops=15181)

Index Cond: (exception.exception_id = "outer".exception_id)Filter: (complete IS FALSE)

Total runtime: 538.76 msec exception表に“WHERE complete IS False”という条件の部分インデックスがあり、条件を満たす行は251行だけなのに使ってくれない

Page 75: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

75

VACUUM ANALYZE後

=# ANALYZE exception;=# EXPLAIN ANALYZE SELECT exception_id FROM exception

JOIN exception_notice_map USING (exception_id)WHERE complete IS FALSE AND notice_id = 3;

QUERY PLAN--------------------------------------------------------------Hash Join (cost=28.48..280.98 rows=1 width=8)

(actual time=31.45..97.78 rows=124 loops=1)Hash Cond: ("outer".exception_id = "inner".exception_id)-> Seq Scan on exception_notice_map

(cost=0.00..250.50 rows=399 width=4)(actual time=0.12..77.12 rows=15181 loops=1)Filter: (notice_id = 3)

-> Hash (cost=26.31..26.31 rows=251 width=4)(actual time=2.96..2.96 rows=0 loops=1)

-> Index Scan using active_exceptions on exception(cost=0.00..26.31 rows=251 width=4)(actual time=0.24..2.55 rows=251 loops=1)

Filter: (complete IS FALSE)Total runtime: 97.99 msec

部分インデックスを使ってくれた

Page 76: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

Explainの実行に対して気を付けておくこと

• まず最初に、テーブルがVACUUMとANALYZEされていることを確かめる

• EXPLAINの出力を確認する

– ANALYZEをつけて出力された実際の行数rowsと推定行数rowsは一致しているか?

– もしも一致していなかったら、統計情報から状態を確認しましょう。(pg_stat_all_tables)

• n_dead_tup(無効タプル数)

• last_vacuum/autovacuum(最後に(auto)vacuumされた時刻)

• last_analyze(最後にanalyzeされた時刻)

• などなど。。

76

Page 77: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

まとめ

• Explainはクエリを実行するために最適と推定された問い合わせプランを表示するもの!

• クエリチューニングをするためにはExplainが正しく読めることが重要!

• 実行が遅いクエリを見つけたら、まずはVACUUM ANALYZEを!

• PostgreSQLは最新のバージョンを使おう!

77

Page 78: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

参考文献• PostgreSQL全機能バイブル

鈴木啓修・著

• Explaining Explain ~ PostgreSQLの実行計画を読む~

by Robert Treat(翻訳:日本PostgreSQLユーザ会)

• Explaining EXPLAIN 第2回

第20回しくみ+アプリケーション勉強会中西さん

• Explaining Explain 第3回

第21回しくみ+アプリケーション勉強会 田中さん

• Let’s PostgreSQL

http://lets.postgresql.jp/

• PostgreSQL wiki

http://wiki.postgresql.org/wiki/Main_Page

• 問合わせ最適化インサイド

NTT オープンソースソフトウェアセンタ 板垣さん

78

Page 79: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

ご清聴ありがとうございました

79

Page 80: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

参考までに

80

Page 81: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

81

選択度の計算

• 計算方法は2通り。

• テーブル内で多く出現(重複)する値を条件指定した場合

Common値(MCV: Most Common Values)

• 実際にその値が入っている行の出現頻度が

分かっているので、そのまま使用する。

上記の例でテーブルtenk1の全行数を1000件とすると、

「Stringu1=‘MCAAAA’」の行数は 「1000×0.003 = 3」 行と推測できる。

# SELECT most_common_vals, most_common_freqs FROM pg_stats WHERE tablename='tenk1' AND attname='stringu1';

most_common_vals | {EJAAAA,BBAAAA,CRAAAA,FCAAAA,FEAAAA,GSAAAA,JOAAAA,MCAAAA,NAAAAA,WGAAAA}

most_common_freqs | {0.00333333,0.003,0.003,0.003,0.003,0.003,0.003,0.003,0.003,0.003}

出現数/テーブル行数

※PostgreSQL 9.0.3 のマニュアル第56章の例を引用

Page 82: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

82

選択度の計算• Common値以外の値を条件指定した場合• 一致検索(WHERE stringu1 = ‘IAAAA’)選択度 = MCV値を除いた行数 / カーディナリティ⇒MCV値以外はテーブル内に均等に出現すると仮定。

• 範囲検索 (WHERE stringu1 < ‘IAAAA’)MCV値以外のヒストグラムを利用

• 指定範囲に含まれるMVC値の選択度と、ヒストグラムから導出した非MVC値の選択度を加える。

# SELECT histogram_bounds FROM pg_statsWHERE tablename='tenk1' AND attname='stringu1';

histogram_bounds--------------------------------------------------------------------------------{AAAAAA,CQAAAA,FRAAAA,IBAAAA,KRAAAA,NFAAAA,PSAAAA,SGAAAA,VAAAAA,XLAAAA,ZZAAAA}

※PostgreSQL 9.0.3 のマニュアル第56章の例を引用

各帯のサンプル数が均一化されるように帯幅を調整

Page 83: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

83

work_mem と lossy storage• ビットマップは作業メモリ(work_mem)上に作成する。

• 通常、ビットマップには行の位置を表すTIDを持つ。– work_mem = 1MB で 500万行ほどしか持てない。

• work_memに収まらないサイズの場合、

lossy storage モードへ移行してビットマップサイズを縮小– 1行を1ビットで表現⇒ 1ページを1ビットで表現(1MBで64GBまでOK)

– 取得データから条件を満たさないデータを排除するコストが必要

0 TID(0, 1)

1 TID(0, 2)

1 TID(0, 3)

0 TID(1, 1)

0 TID(1, 2)

0 TID(1, 3)

・・ ・・

1 ページ0

0 ページ1

・・ ・・

通常モード lossy storage モード

ページ単位のビットマップに変更してサイズ縮小

TID(0, 1)

TID(0, 2)

TID(0, 3)

・・

行データを取ってから不要行を削除する

Page 84: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

84

・・・

TIDとは

• システムによって暗黙的に定義されたシステム列。

• 行データの位置(格納ブロック,アイテムポインタ位置)を示す。

索引

キー値:1000 TID=(5,1) キー値:1001 TID=(5,2) ・・・

ブロック番号 5

ブロック(ページ)ヘッダ アイテムポインタ1 アイテムポインタ2 ・・・

行データ1(キー値:1000)行データ2(キー値:1001)・・・

TIDを1つ指定して検索する場合のコスト:random_page_cost(4.00)+ cpu_tuple_cost(0.01) = 4.01

ただし、行のctidはレコードが更新されたり、VACUUM FULLで移動させられると変わるので、直接TIDを指定して検索するケースは少ないと思われる。

Page 85: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

85

Function Scan 演算子

• 関数がデータをgatherするときに出てくる• トラブルシューティングの観点からは若干ミステリアス• 関数の中で使われているクエリについてexplainを走らせるべき

=# CREATE FUNCTION foo(integer) RETURNS SETOF integer AS$$

select $1;$$LANGUAGE sql;

=# EXPLAIN SELECT * FROM foo(12);QUERY PLAN

------------------------------------------------------------Function Scan on foo (cost=0.00..12.50 rows=1000 width=4)

Page 86: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

86

Unique 演算子

• 入力セットから重複する値を削除• 行の並べ替えはせず、単に重複する行を取り除く• 入力セットは予めソート済み (Sort演算子の後に行う)• タプルコストごとに「CPU演算」×2• DISTINCT と UNION で使用される

=# EXPLAIN SELECT distinct oid FROM pg_proc;QUERY PLAN

--------------------------------------------------Unique (cost=181.55..190.29 rows=1747 width=4)-> Sort (cost=181.55..185.92 rows=1747 width=4)

Sort Key: oid-> Seq Scan on pg_proc

(cost=0.00..87.47 rows=1747 width=4)

Page 87: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

87

Aggregate 演算子

• count, sum, min, max, avg, sttdev, varianceを使用

• GROUP BY 使用の場合差異が認められることがあり

=# EXPLAIN SELECT count(*) FROM pg_proc;QUERY PLAN

--------------------------------------------------------------Aggregate (cost=91.84..91.84 rows=1 width=0)-> Seq Scan on pg_proc (cost=0.00..87.47 rows=1747 width=0)

=# EXPLAIN SELECT count(oid), oid FROM pg_proc GROUP BY oid;QUERY PLAN

-------------------------------------------------------------HashAggregate (cost=96.20..100.57 rows=1747 width=4)-> Seq Scan on pg_proc (cost=0.00..87.47 rows=1747 width=4)

Page 88: PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

88

GroupAggregate 演算子

• GROUP BYを使用し、より大きな結果セット上に集約を行う

=# EXPLAIN SELECT count(*) FROM pg_foo GROUP BY oid;QUERY PLAN

-----------------------------------------------------------------GroupAggregate (cost=37442.53..39789.07 rows=234654 width=4)-> Sort (cost=37442.53..38029.16 rows=234654 width=4)

Sort Key: oid-> Seq Scan on pg_foo (cost=0.00..13520.54 rows=234654 width=4)