pg_hint_plan -- controls execution plan with Hint information in a particular comment
PostgreSQL uses cost based optimizer. Based on SQL statement and statistics, cost of possible access path is estimated and execution plan with lowest cost is selected. Optimizer tries to use best execution plan make as far as possible but as it does not refer to the correlation etc., between columns, it cannot be said that in case of complicated queries always proper plan will get selected.
If pg_hint_plan is used then without changing SQL statement or GUC parameter, execution plan can be controlled by adding block comment having description of hint, before the SQL sentence.
Write the Hint in the beginning of query character string in block comment. In order to make the block comment recognize as Hint, need to specify plus (+) immediately in the opening of block comment. Specify the Hint targeted items by object name or alias name in brackets. Specify the object name by differentiating it by using space, tab or new line.
In the example below , Hash Join execution plan is selected for the SeqScan for pgbench_accounts table from HashJoin and SeqScan hint
postgres=# /*+ postgres*# HashJoin(a b) postgres*# SeqScan(a) postgres*# */ postgres-# EXPLAIN SELECT * postgres-# FROM pgbench_branches b postgres-# JOIN pgbench_accounts a ON b.bid = a.bid postgres-# ORDER BY a.aid; QUERY PLAN --------------------------------------------------------------------------------------- Sort (cost=31465.84..31715.84 rows=100000 width=197) Sort Key: a.aid -> Hash Join (cost=1.02..4016.02 rows=100000 width=197) Hash Cond: (a.bid = b.bid) -> Seq Scan on pgbench_accounts a (cost=0.00..2640.00 rows=100000 width=97) -> Hash (cost=1.01..1.01 rows=1 width=100) -> Seq Scan on pgbench_branches b (cost=0.00..1.01 rows=1 width=100) (7 rows) postgres=#
Hint that can be used in pg_hint_plan, is divided into 4 groups which are scan method and integration method, integration sequence, GUC parameter. For specific Hint of each group see Hint list
With these hints , you can force or restraint the optimizer to select specific scan method for tables or columns.It includes 'SeqScan' , 'IndexScan' etc.
Usual table, inheritance table, UNLOGGED table, temporary table, system catalog can be specified Scan method. And external table, table function, VALUES command result, CTE, View, Sub-enquiry result cannnot be.
With these hints , you can force or restraint the optimizer to select specific join method for combination of tables.It includes 'MergeJoin’ and ‘NestLoop’ etc.MergeJoin’ and ‘NestLoop’ etc.
usual tale, inheritance table, UNLOGGED table, temporary table, external table, system catalog, table function, VALUES command result, CTE can be specified join method. And view and sub inquiry result cannot be.
‘Leading’hint forces join order in which table is specified.
'Set' hint changes GUC parameters just while creating execution plan for this query.
Only GUC parameter of Query Planning will have the effect. If Hint of multiple GUC parameters is specified for same GUC parameter, Hint specified in the last will be applicable.
You can use GUC of pg_hint_plan in 'Set' hint,but since it does not work as per expectation, it is recommended that not to specify. See also Restrictions.
The GUC parameters for pg_hint_planpg_hint_plan is below.
GUC | discription | Default |
---|---|---|
pg_hint_plan.enable_hint | Enbles or disables the function of pg_hint_plan. | on |
pg_hint_plan.debug_print | Enables the debug output of pg_hint_plan. Since message will be output at LOG message level, by default it will be output in server log and will not be passed to the client. | off |
pg_hint_plan.parse_messages | When specified Hint cannot be interpret, then in which message level log should be output, is specified. Valid values are debug5, debug4, debug3, debug2, debug1, log, info, notice, warning, or error. Fatal and panic cannot be specified. |
For PostgreSQL 9.1 , it is required to set these parameters in postgresql.conf file to add 'pg_hint_plan' for custom_variable_classes. Typical usage example of this is below.
# postgresql.conf shared_preload_libraries = 'pg_hint_plan' custom_variable_classes = 'pg_hint_plan' # not required for 9.2 or after pg_hint_plan.parse_messages = 'debug2'
Since PostgreSQL 9.2 onwards custom_variable_classes are deleted, it can be written similarly as GUC parameter which is based on GUC parameter of pg_hint_plan standards
Installation of pg_hint_plan.
To build pg_hint_plan from source code , in the pg_hint_plan's source directory "make → make install" by the OS user in which PostgreSQL is installed. Since pgxs is used for building of pg_hint_plan, postgresql-devel package is needed in the environment where PostgreSQL of RPM version is used.
Example of build is given below
$ tar xzvf pg_hint_plan-1.0.0.tar.gz $ cd pg_hint_plan-1.0.0 $ make $ su # make install
When using the pg_hint_plan in a particular session only, load shared library of pg_hint_plan by using LOAD command as shown in the below example. When using as a general user, be careful as it needs to be installed in $libdir/plugins aswell.
postgres=# LOAD 'pg_hint_plan'; LOAD postgres=#
To activate pg_hint_plan in all sessions, add ‘pg_hint_plan’ to shared_preload_libraries GUC parameter and then re-start the server.
To uninstall pg_hint_plan, run ‘make uninstall’ in directory in which pg_hint_plan source is deployed. Run ‘make uninstall’ by the OS user in which PostgreSQL is installed.
Example of un-install below
$ cd pg_hint_plan-1.0.0 $ su # make uninstall
In the Hint of Scan method, table to be scanned is specified. When wish to use particular index in IndexScan Hint, index can also be specified in options. In the following example, table1 selects Seq Scan, table2 selects Index Scan in main key index.
postgres=# /*+ postgres*# SeqScan(t1) postgres*# IndexScan(t2 t2_pkey) postgres*# */ postgres-# SELECT * FROM table1 t1 JOIN table table2 t2 ON (t1.key = t2.key); ...
In the Hint of join method or join order list of tables to be joined is specified. Following example shows the case that make table1 and tabele2 join directory with Nested Loop and makes table1, table2 and table3 join with Merge Join . But, depending on the cost estimation, there can be a case wherein table1 and table2 cannot be integrated directly hence Leading Hint is used simultaneously in such way that table1 and table2 are joined first and then table3 is joined.
postgres=# /*+ postgres*# NestLoop(t1 t2) postgres*# MergeJoin(t1 t2 t3) postgres*# Leading(t1 t2 t3) postgres*# */ postgres-# SELECT * FROM table1 t1 postgres-# JOIN table table2 t2 ON (t1.key = t2.key) postgres-# JOIN table table3 t3 ON (t2.key = t3.key); ...
In the Hint of GUC parameter, pair of GUC parameter and value is specified. In the following example, only while creating execution plan of this query change random_page_cost to 2.0.
postgres=# /*+ postgres*# Set(random_page_cost 2.0) postgres*# */ postgres-# SELECT * FROM table1 t1 WHERE key = 'value'; ...
postgres=# /*+ postgres*# HashJoin(a b) postgres*# SeqScan(a) postgres*# */ postgres-# /*+ IndexScan(a) */ postgres-# EXPLAIN SELECT /*+ MergeJoin(a b) */ * postgres-# FROM pgbench_branches b postgres-# JOIN pgbench_accounts a ON b.bid = a.bid postgres-# ORDER BY a.aid; QUERY PLAN --------------------------------------------------------------------------------------- Sort (cost=31465.84..31715.84 rows=100000 width=197) Sort Key: a.aid -> Hash Join (cost=1.02..4016.02 rows=100000 width=197) Hash Cond: (a.bid = b.bid) -> Seq Scan on pgbench_accounts a (cost=0.00..2640.00 rows=100000 width=97) -> Hash (cost=1.01..1.01 rows=1 width=100) -> Seq Scan on pgbench_branches b (cost=0.00..1.01 rows=1 width=100) (7 rows) postgres=#
postgres=# /*+ HashJoin(t1 t1)*/ postgres-# EXPLAIN SELECT * FROM s1.t1 postgres-# JOIN public.t1 ON (s1.t1.id=public.t1.id); INFO: hint syntax error at or near "HashJoin(t1 t1)" DETAIL: Relation name "t1" is ambiguous. QUERY PLAN ------------------------------------------------------------------ Merge Join (cost=337.49..781.49 rows=28800 width=8) Merge Cond: (s1.t1.id = public.t1.id) -> Sort (cost=168.75..174.75 rows=2400 width=4) Sort Key: s1.t1.id -> Seq Scan on t1 (cost=0.00..34.00 rows=2400 width=4) -> Sort (cost=168.75..174.75 rows=2400 width=4) Sort Key: public.t1.id -> Seq Scan on t1 (cost=0.00..34.00 rows=2400 width=4) (8 行) postgres=# /*+ HashJoin(pt st) */ postgres-# EXPLAIN SELECT * FROM s1.t1 st postgres-# JOIN public.t1 pt ON (st.id=pt.id); QUERY PLAN --------------------------------------------------------------------- Hash Join (cost=64.00..1112.00 rows=28800 width=8) Hash Cond: (st.id = pt.id) -> Seq Scan on t1 st (cost=0.00..34.00 rows=2400 width=4) -> Hash (cost=34.00..34.00 rows=2400 width=4) -> Seq Scan on t1 pt (cost=0.00..34.00 rows=2400 width=4) (5 行) postgres=#
postgres=# CREATE VIEW view1 AS SELECT * FROM table1 t1; CREATE TABLE postgres=# /*+ SeqScan(t1) */ postgres=# EXPLAIN SELECT * FROM table1 t1 JOIN view1 t2 ON (t1.key = t2.key) WHERE t2.key = 1; QUERY PLAN ----------------------------------------------------------------- Nested Loop (cost=0.00..358.01 rows=1 width=16) -> Seq Scan on table1 t1 (cost=0.00..179.00 rows=1 width=8) Filter: (key = 1) -> Seq Scan on table1 t1 (cost=0.00..179.00 rows=1 width=8) Filter: (key = 1) (5 rows) postgres=# /*+ SeqScan(t3) */ postgres=# EXPLAIN SELECT * FROM table1 t3 JOIN view1 t2 ON (t1.key = t2.key) WHERE t2.key = 1; QUERY PLAN -------------------------------------------------------------------------------- Nested Loop (cost=0.00..187.29 rows=1 width=16) -> Seq Scan on table1 t3 (cost=0.00..179.00 rows=1 width=8) Filter: (key = 1) -> Index Scan using foo_pkey on table1 t1 (cost=0.00..8.28 rows=1 width=8) Index Cond: (key = 1) (5 rows)
postgres=# /*+ MergeJoin(a *VALUES*) */ postgres-# EXPLAIN SELECT * postgres-# FROM pgbench_accounts a postgres-# JOIN (VALUES (1,1),(2,2)) v (vid, vbalance) ON a.aid = v.vid postgres-# ORDER BY a.aid; QUERY PLAN --------------------------------------------------------------------------------------------------------------- Merge Join (cost=0.04..4497.33 rows=2 width=105) Merge Cond: (a.aid = "*VALUES*".column1) -> Index Scan using pgbench_accounts_pkey on pgbench_accounts a (cost=0.00..4247.26 rows=100000 width=97) -> Sort (cost=0.04..0.04 rows=2 width=8) Sort Key: "*VALUES*".column1 -> Values Scan on "*VALUES*" (cost=0.00..0.03 rows=2 width=8) (6 rows) postgres=#