こんにちは。サイオステクノロジー OSS サポート担当 Y です。
今回は、PostgreSQL の enable_seqscan を off にした際の処理について調査を行なってみました。(※以下の内容は CentOS 7.6/PostgreSQL 11.1 にて検証しています。)
■はじめに
前回は、ドキュメントに記載されている “enable_seqscan を off にしても、シーケンシャルスキャンを完全に無効にすることはできない” という内容について検証を行いました。
結果として、ドキュメントに記載されている通りシーケンシャルスキャンを完全に無効にすることはできない (enable_seqscan = off であっても、シーケンシャルスキャンが実行されることがある) ことを確認できました。
今回は、この “完全に無効にすることはできない” 部分の仕組みを少し深掘りしてみようかと思います。
■調査
ということで、仕組みの詳細を深掘りするためにソースコードを調べてみようと思います。
今回調査の対象となる enable_seqscan の設定値は、プログラムの内部でも同名の変数 enable_seqscan に格納されて利用されているようです。
[src/backend/utils/misc/guc.c 抜粋] 820 { 821 {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD, 822 gettext_noop("Enables the planner's use of sequential-scan plans."), 823 NULL 824 }, 825 &enable_seqscan, 826 true, 827 NULL, NULL, NULL 828 },
この変数 enable_seqscan が利用されている箇所を探してみると、以下の様な処理を発見しました。
[src/backend/optimizer/path/costsize.c 抜粋] 204 /* 205 * cost_seqscan 206 * Determines and returns the cost of scanning a relation sequentially. 207 * 208 * 'baserel' is the relation to be scanned 209 * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL 210 */ 211 void 212 cost_seqscan(Path *path, PlannerInfo *root, 213 RelOptInfo *baserel, ParamPathInfo *param_info) 214 { 215 Cost startup_cost = 0; ~(中略)~ 232 if (!enable_seqscan) 233 startup_cost += disable_cost; ~(中略)~ 277 path->startup_cost = startup_cost; 278 path->total_cost = startup_cost + cpu_run_cost + disk_run_cost; 279 }
enable_seqscan = off の設定 (変数 enable_seqscan の値が false) の場合、”startup_cost += disable_cost” という計算が実施されています。
また、(かなり大雑把な説明にはなってしまいますが) この startup_cost 等の値を使って、最終的な実行計画のコストが計算されるようです。
上記ソースコードを見てみると、enable_seqscan の on/off によって処理が分岐し、この “コストの計算” の部分 (コストの計算結果) が変わる仕組みになっているようです。
この計算に使われている disable_cost の値は以下の様に定義されていました。1.0e10 = 10000000000 なので、前回の検証で出力されていた “cost=10000000000.00” という内容とも合致しているようです。
[src/backend/optimizer/path/costsize.c 抜粋] 121 Cost disable_cost = 1.0e10;
上記より、”enable_seqscan = off” が設定されている場合 “シーケンシャルスキャンを行う実行計画のコストを大きくする” という処理が実行されるようです。
PostgreSQL は、クエリを実行する際に実行可能な複数の実行計画のコストを計算し、基本的に最もコストが低い実行計画を選んでその実行計画でクエリを実行します。
つまり、”enable_seqscan = off” の場合 “シーケンシャルスキャンを行う実行計画のコストが大きくなる” ため、シーケンシャルスキャンが選択されにくくなるという動作になるようです。
■まとめ
ソースコードを読んでみたところ、”enable_seqscan = off” に設定しても “シーケンシャルスキャンを利用する実行計画のコストが大きくなる” だけであり、”他の実行計画のコストの方が大きい場合” や “シーケンシャルスキャン以外の選択肢 (実行計画) が無い場合” には、シーケンシャルスキャンが実行される作りになっているようです。
これが、今回調査しようとしていた “シーケンシャルスキャンを完全に無効にはできない” という部分の仕組み (理由) のようです。