Contenu connexe
Similaire à pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料) (20)
Plus de NTT DATA Technology & Innovation (20)
pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)
- 1. © 2022 NTT DATA Corporation
pg_hint_plan を知る
2022/12/20 第37回 PostgreSQLアンカンファレンス@オンライン
株式会社NTTデータ 技術開発本部
葛木優太
- 2. © 2022 NTT DATA Corporation 2
自己紹介
• 葛木優太 (@katsuragi_yuta)
• NTTデータ 技術開発本部 所属
• 新卒1年目
- 3. © 2022 NTT DATA Corporation 3
目次
• pg_hint_plan の概要
• pg_hint_plan とは
• pg_hint_plan の使い方
• pg_hint_plan の仕組み
- 4. © 2022 NTT DATA Corporation 4
01
pg_hint_plan の概要
- 5. © 2022 NTT DATA Corporation 5
pg_hint_plan とは
• pg_hint_plan とは
• NTT OSS センタが中心となって開発している PostgreSQL のエクステンション
• クエリにヒントを指定することで、SQL や GUC パラメータを変更せず実行計画を制御
• 現時点 (2022/12/20) での最新版は 1.5 で PostgreSQL 15 をサポート
• 例えば次のようなことが可能
• 特定のテーブルのスキャン方法を指定
• 特定のテーブル同士の結合方法を指定
• 特定のテーブル同士の結合順序を指定
• ヒントを記述した SQL 文を処理する間だけ GUC パラメータを変更
# /*+ MergeJoin(t1 t2) */
# SELECT * FROM t1, t2 WHERE t1.x = t2.x;
t1 と t2 の結合方法を指定するヒント句
本資料では pg_hint_plan 1.4 と
PostgreSQL 14 を扱う
- 6. © 2022 NTT DATA Corporation 6
pg_hint_plan とは | ヒントとヒント句
• pg_hint_plan では SQL に対してヒントを指定して実行計画を制御
• ヒントはヒント句から構成される
• pg_hint_plan では 2 つの場所にヒントを記述可能
• SQL のコメント
• hint table
MergeJoin(t1 t2) IndexScan(t1 t1_pkey) IndexScan(t2 t2_pkey)
t1 と t2 の結合方法を指定するヒント句 t1 のスキャン方法を指定するヒント句 t2 のスキャン方法を指定するヒント句
これら 3 つのヒント句をまとめてヒントと呼ぶ
- 7. © 2022 NTT DATA Corporation 7
pg_hint_plan とは | SQL のコメントとしてのヒント
• SQL の先頭にコメントの形式でヒントを記述する方法
• コメントは /*+ で開始し、*/ で終わる必要がある
QUERY PLAN
----------------------------------------------------------------------------
Merge Join (cost=0.42..19.72 rows=100 width=16)
Merge Cond: (t1.id = t2.id)
-> Index Scan using t1_pkey on t1 (cost=0.14..13.64 rows=100 width=8)
-> Index Scan using t2_pkey on t2 (cost=0.28..43.27 rows=1000 width=8)
=# /*+ MergeJoin(t1 t2) IndexScan(t1 t1_pkey) IndexScan(t2 t2_pkey) */
-# EXPLAIN SELECT * FROM t1,t2 WHERE t1.id = t2.id;
- 8. © 2022 NTT DATA Corporation 8
pg_hint_plan とは | hint table
• SQL に対するヒント句を hint table (hint_plan.hints) に登録
• ヒントと、ヒントの適用対象となるクエリを保持するテーブル
• デフォルトではスーパーユーザのみ編集可能
• norm_query_string では空白、セミコロンの有無も区別される
• hint table に記述したヒントは、コメントより優先されるが、その場合、コメント付きの SQL を登録する必要がある
=# INSERT INTO hint_table.hints(norm_query_string, application_name, hints) VALUES
-# (‘EXPLAIN SELECT * FROM t1 WHERE t1.id = ?;’,
-# ‘’,
-# 'SeqScan(t1)’
-# );
- 9. © 2022 NTT DATA Corporation 9
pg_hint_plan の使い方 | インストールと設定
• ビルド
• make; make install
• postgresql.conf
• hint table を使用する場合の追加の設定
shared_preload_libraries = ‘pg_hint_plan’
pg_hint_plan.enable_hint = on
=# CREATE EXTENSION pg_hint_plan;
=# SET pg_hint_plan.enable_hint_table TO on;
(*) その他の pg_hint_plan 固有の GUC パラメータはドキュメントを参照
https://github.com/ossc-db/pg_hint_plan/blob/master/doc/pg_hint_plan-ja.html
デフォルトで on に設定される
- 10. © 2022 NTT DATA Corporation 10
pg_hint_plan の使い方
• 簡単にヒント句の使い方を紹介
• ヒント句の一覧についてはドキュメントを参照 (*)
(*) https://github.com/ossc-db/pg_hint_plan/blob/master/doc/hint_list-ja.html
- 11. © 2022 NTT DATA Corporation 11
pg_hint_plan の使い方 | スキャン方法の指定
• SeqScan を指定する場合
• IndexScan を指定する場合
=# /*+ SeqScan(t1) */
-# EXPLAIN SELECT * FROM t1 WHERE t1.id = 10;
QUERY PLAN
--------------------------------------------------
Seq Scan on t1 (cost=0.00..2.25 rows=1 width=8)
Filter: (id = 10)
=# /*+ IndexScan(t1 t1_pkey) */
-# EXPLAIN SELECT * FROM t1 WHERE t1.id = 10;
QUERY PLAN
------------------------------------------------------------------
Index Scan using t1_pkey on t1 (cost=0.14..8.16 rows=1 width=8)
Index Cond: (id = 10)
使用したいインデックス名のリストを空白区切りで記述
- 12. © 2022 NTT DATA Corporation 12
pg_hint_plan の使い方 | 結合方法の指定
結合方法と、対象とするテーブルを記述
• NestLoop(t1 t2) は t1 と t2 の結合を NestLoop で実行することを指定
• NestLoop(t1 t2) と NestLoop(t2 t1) は等価
• 3 テーブル以上の結合が必要な場合、NestLoop(t1 t2) は t1 と t2 の結合を強制するわけではない
=# /*+ NestLoop(t1 t2) */
-# EXPLAIN SELECT * FROM t1,t2 WHERE t1.id = t2.id;
QUERY PLAN
------------------------------------------------------------------------
Nested Loop (cost=0.28..71.25 rows=100 width=16)
-> Seq Scan on t1 (cost=0.00..2.00 rows=100 width=8)
-> Index Scan using t2_pkey on t2 (cost=0.28..0.69 rows=1 width=8)
Index Cond: (id = t1.id)
- 13. © 2022 NTT DATA Corporation 13
pg_hint_plan の使い方 | 結合方法の指定
結合方法と、対象とするテーブルを記述
• NestLoop(t1 t2 t3) は t1、t2、t3 の結合に対して有効
• (t1 t2)、(t1 t3)、(t2 t3) の結合には有効ではない
=# /*+ NestLoop(t1 t2 t3) */
-# EXPLAIN SELECT * FROM t1,t2,t3 WHERE t1.id = t2.id and t2.id = t3.id;
QUERY PLAN
------------------------------------------------------------------------------------
Nested Loop (cost=5.88..41.44 rows=100 width=24)
Join Filter: (t1.id = t2.id)
-> Merge Join (cost=5.61..10.54 rows=100 width=16)
Merge Cond: (t3.id = t1.id)
-> Index Scan using t3_pkey on t3 (cost=0.29..318.29 rows=10000 width=8)
-> Sort (cost=5.32..5.57 rows=100 width=8)
Sort Key: t1.id
-> Seq Scan on t1 (cost=0.00..2.00 rows=100 width=8)
-> Index Scan using t2_pkey on t2 (cost=0.28..0.30 rows=1 width=8)
Index Cond: (id = t3.id)
- 14. © 2022 NTT DATA Corporation 14
pg_hint_plan の使い方 | 結合方法の指定
結合方法と、対象とするテーブルを記述
• NestLoop(t1 t2 t3) は t1、t2、t3 の結合に対して有効
• (t1 t2)、(t1 t3)、(t2 t3) の結合には有効ではない
=# /*+ NestLoop(t1 t2 t3) */
-# EXPLAIN SELECT * FROM t1,t2,t3 WHERE t1.id = t2.id and t2.id = t3.id;
Merge Join t2
IndexScan
t3 t1
IndexScan SeqScan
左のプランツリーに含まれるテーブル: t1、t3
右のプランツリーに含まれるテーブル: t2
t3 t1
IndexScan SeqScan
左のプランツリーに含まれるテーブル: t3
右のプランツリーに含まれるテーブル: t1
NestLoop(t1 t2 t3) の適用対象
⋈
⋈
NestLoop(t1 t2 t3) の適用対象ではない
左右のプランツリーに含まれるテーブルが
t1、t2、t3 だけであるということ
- 15. © 2022 NTT DATA Corporation 15
pg_hint_plan の使い方 | 結合順の指定
• Leading 句により結合順を指定
• 指定方法は 2 種類
• 結合順のみ指定
• 外部表と内部表を含めて結合順を指定
- 16. © 2022 NTT DATA Corporation 16
pg_hint_plan の使い方 | 結合順の指定
結合順のみを指定する場合
• Leading 句に結合したいテーブルを、その順に記述
• Leading(t1 t2 t3) により、t1 と t2 の結合を実施してから t3 との結合を実施する
=# /*+ Leading(t1 t2 t3) */
-# EXPLAIN SELECT * FROM t1,t2,t3 WHERE t1.id = t2.id and t2.id = t3.id;
QUERY PLAN
----------------------------------------------------------------------------------
Merge Join (cost=5.88..16.61 rows=100 width=24)
Merge Cond: (t1.id = t3.id)
-> Merge Join (cost=5.60..11.65 rows=100 width=16)
Merge Cond: (t2.id = t1.id)
-> Index Scan using t2_pkey on t2 (cost=0.28..43.27 rows=1000 width=8)
-> Sort (cost=5.32..5.57 rows=100 width=8)
Sort Key: t1.id
-> Seq Scan on t1 (cost=0.00..2.00 rows=100 width=8)
-> Index Scan using t3_pkey on t3 (cost=0.29..318.29 rows=10000 width=8)
- 17. © 2022 NTT DATA Corporation 17
pg_hint_plan の使い方 | 結合順の指定
外部表と内部表を含めて結合順を指定する場合
• Leading 句で明示的に外部表と内部表を指定
Leading(( t1 (t3 t2) ))
Leading(( outer-table-set inner-table-set ))
outer-table-set := table-set
inner-table-set := table-set
table-set := table-name | (outer-table-set inner-table-set)
ヒント句の書き方は以下
ただし、
例)
t2
t3
t1
⋈
⋈
- 18. © 2022 NTT DATA Corporation 18
pg_hint_plan の使い方 | 結合順の指定
外部表と内部表を含めて結合順を指定する場合
• Leading 句で明示的に外部表と内部表を指定
=# /*+ Leading(( t1 (t2 t3) )) NestLoop(t1 t2 t3) NestLoop(t2 t3) */
-# EXPLAIN SELECT * FROM t1,t2,t3 WHERE t1.id = t2.id and t2.id = t3.id;
QUERY PLAN
------------------------------------------------------------------------------------
Nested Loop (cost=0.29..2122.00 rows=100 width=24)
Join Filter: (t2.id = t1.id)
-> Seq Scan on t1 (cost=0.00..2.00 rows=100 width=8)
-> Materialize (cost=0.29..622.50 rows=1000 width=16)
-> Nested Loop (cost=0.29..617.50 rows=1000 width=16)
-> Seq Scan on t2 (cost=0.00..15.00 rows=1000 width=8)
-> Index Scan using t3_pkey on t3 (cost=0.29..0.60 rows=1 width=8)
Index Cond: (id = t2.id)
(t2 t3)
t1 (t2 t3)
- 19. © 2022 NTT DATA Corporation 19
02
pg_hint_plan の仕組み
- 20. © 2022 NTT DATA Corporation 20
ここから
• 以下の2点に着目し、pg_hint_plan の動作イメージを追う
• どこでヒントが解析されるか
• ヒントの情報からどのようにプランナを制御するか
• スキャン方法の指定
• 結合方法の指定
• 結合順序の指定
• 前提
• SQL は /*+ HINT */ SELECT * FROM t1, t2, t3 WHERE t1.id = t2.id AND t2.id = t3.id
共通する大まかな方針は、
適切なタイミングで適切に GUC パラメータを変更する
- 21. © 2022 NTT DATA Corporation 21
PostgreSQL のクエリ処理系と pg_hint_plan の関係
パーサ
プランナ
エグゼキュータ
SQL
実行結果
アクセスパスを列挙
結合方法・順序を探索
pg_hint_plan が有効でない場合
- 22. © 2022 NTT DATA Corporation 22
PostgreSQL のクエリ処理系と pg_hint_plan の関係
パーサ
プランナ
エグゼキュータ
SQL
実行結果
アクセスパスを列挙
結合方法・順序を探索
pg_hint_plan が有効な場合
ヒントの解析
ヒント句のための処理
(GUC の変更など)
pg_hint_plan
pg_hint_plan
ヒント句のための処理
(GUC の変更など)
pg_hint_plan
- 23. © 2022 NTT DATA Corporation 23
t1
Merge
Merge
t2
t3 …
t2
NestLoop
NestLoop
t3
t1
プランナの動作イメージ | pg_hint_plan が有効でない場合
…
Hash
t2 t3
Merge
t2 t3
NestLoop
t2 t3
…
結合方法・結合順を探索
エグゼキュータへ
t1 t2 t3
IndexScan
SeqScan
アクセスパスを列挙
パーサ
Hash
t1 t2
Merge
t1 t2
NestLoop
t1 t2
… …
- 24. © 2022 NTT DATA Corporation 24
プランナの動作イメージ | pg_hint_plan が有効な場合
/*+ HINT */
SELECT ...
FROM t1,t2,t3
WHERE ...
…
t1
NestLoop
NestLoop
t2
t3
GUC
enable_nestloop on
enable_mergejoin off
enable_hashjoin off
…
Hash
t2 t3
Merge
t2 t3
NestLoop
t2 t3
…
NestLoop
t1 t2
GUC
enable_nestloop on
enable_mergejoin off
enable_hashjoin off
…
結合方法・結合順を探索
ヒントの解析
エグゼキュータへ
t1 t2 t3
IndexScan
SeqScan
GUC
enable_seqscan on
enable_indexscan off
...
t1 t2 t3
SeqScan
t1 t2 t3
アクセスパスを列挙
パーサ
…
- 25. © 2022 NTT DATA Corporation 25
ヒントの解析
• planner_hook で実施
• ヒント句に対応する構造体を作成
• コメントを先頭から読んでいき、キーワード (e.g. SeqScan) に対応するパーサを呼ぶ
• ヒントタイプごとにグルーピングして管理
• ヒント句の重複判定などに用いる
NestLoop(t1 t2) NestLoop(t1 t2 t3)
IndexScan(t1 t1_pkey) SeqScan(t2) SeqScan(t3) SeqScan(t1)
NestLoop(t1 t2)
HintState
NestLoop
(t1 t2 t3)
IndexScan(t1)
hint_str
nall_hints 6
all_hints
SeqScan(t1)
ScanMethodHint ScanMethodHint JoinMethodHint JoinMethodHint
…
スキャンのヒント句 結合方法のヒント句
- 26. © 2022 NTT DATA Corporation 26
t1 t2 t3
IndexScan
SeqScan
GUC
enable_seqscan on
enable_indexscan off
...
t1 t2 t3
SeqScan
t1 t2 t3
スキャン方法の指定
• set_rel_pathlist_hook で実施
• 適用すべきヒント句がある場合、GUC パラメータやテーブルのインデックス一覧をヒント句の内容に応じて書き換えて
プランナにアクセスパスを生成させる
set_rel_pathlist_hook
アクセスパスを列挙
プランナが生成した
アクセスパスのリストを消去
例)SeqScan(t1) が指定された場合
ヒント句に対応する処理
• ScanMethodHint をもとに GUC パラメータを変更
• プランナから不必要なインデックスが
見えないように、インデックスの情報を削除
(t1 の RelOptInfo から IndexOptInfo を削除)
再度、プランナにアクセスパスの
リストを生成させる
- 27. © 2022 NTT DATA Corporation 27
結合方法の指定
• join_search_hook で実施
• 指定されたテーブル同士の結合方法を考えるタイミングで GUC パラメータを変更
…
t1
NestLoop
NestLoop
t2
t3
GUC
enable_nestloop on
enable_mergejoin off
enable_hashjoin off
…
Hash
t2 t3
Merge
t2 t3
NestLoop
t2 t3
…
NestLoop
t1 t2
GUC
enable_nestloop on
enable_mergejoin off
enable_hashjoin off
…
結合方法・結合順を探索
例)NestLoop(t1 t2) NestLoop(t1 t2 t3) が指定された場合
t1 と t2 の結合方法を探索する間
だけ、GUC パラメータを変更
t1 と t2 と t3 の結合方法を探索する間
だけ、GUC パラメータを変更
ヒント句が指定されていないテーブル
同士は通常通り
- 28. © 2022 NTT DATA Corporation 28
結合順の指定
• 指定した結合順を強制するような JoinMethodHint に変換
• 加えて、望ましくないテーブル間での結合方法を探索する間は、
NestLoop 以外のすべての結合方法を禁止するように GUC パラメータを変更
例)Leading(t1 t2 t3) が指定された場合
Leading(t1 t2 t3)
NestLoop(t1 t2)
MergeJoin(t1 t2)
HashJoin(t1 t2)
NestLoop(t1 t2 t3)
MergeJoin(t1 t2 t3)
HashJoin(t1 t2 t3)
右のようなヒント句の作成に加えて、
この順に反する結合の探索では (t1 と t3 や t2 と t3)、
enable_nestloop/enable_mergejoin/enable_hashjoin がす
べて off に設定される
- 29. © 2022 NTT DATA Corporation 29
まとめ
この資料では
• pg_hint_plan について、使い方と仕組みの観点から簡単に紹介した
所感
• 複雑に見えるプランナの動作を、シンプルな仕組みで制御していて面白いと感じた
• pg_hint_plan のソースコードを読むことで、PostgreSQL のプランナについての理解も深まっていくと感じた
- 30. © 2022 NTT DATA Corporation
その他、記載されている会社名、商品名、又はサービス名は、
各社の登録商標又は商標です。