SoFunction
Updated on 2025-04-08

A brief discussion on pg_hint_plan custom execution plan

Sometimes the execution plan given by PG is not optimal for many reasons. When we need to manually specify the execution path, we can load the plug-in pg_hint_plan.

1 Install the plug-in

Preinstall Postgresql10.7

cd postgresql-10.7/contrib/
wget /ossc-db/pg_hint_plan/archive/REL10_1_3_3.
tar xzvf pg_hint_plan-REL10_1_3_3.
cd pg_hint_plan-REL10_1_3_3
make
make install

Check the file

cd $PGHOME
ls lib/pg_hint_plan.so
lib/pg_hint_plan.so
ls share/extension/
pg_hint_plan--1.3.0--1.3. pg_hint_plan--1.3.2--1.3. pg_hint_plan.control 
pg_hint_plan--1.3.1--1.3. pg_hint_plan--1.3.   plpgsql--1.  plpgsql--unpackaged--1.

2 Loading the plugin

2.1 The current session is loading

LOAD 'pg_hint_plan';

Note that this loading only takes effect on the current reply.

2.2 User and library level automatic loading

alter user postgres set session_preload_libraries='pg_hint_plan';
alter database postgres set session_preload_libraries='pg_hint_plan';

If the configuration is wrong, you won't even go to the database!

If the configuration is wrong, connect to the template1 library to execute

alter database postgres reset session_preload_libraries;
alter user postgres reset session_preload_libraries;

2.3 cluster-level automatic loading

Modify inshared_preload_libraries=‘pg_hint_plan'

Restart the database

3 Check if it has been loaded

After pg_hint_plan is loaded, it cannot be seen in the extension, so you need to confirm whether the plug-in has been loaded.

show session_preload_libraries;
 session_preload_libraries
---------------------------
 pg_hint_plan

or

show shared_preload_libraries;

If loading is used, there is no need to check it.

4 Use plugins to customize execution plans

4.1 Initialize the test data

create table t1 (id int, t int, name varchar(255));
create table t2 (id int , salary int);
create table t3 (id int , age int);
insert into t1 values (1,200,'jack');
insert into t1 values (2,300,'tom');
insert into t1 values (3,400,'john');
insert into t2 values (1,40000);
insert into t2 values (2,38000);
insert into t2 values (3,18000);
insert into t3 values (3,38);
insert into t3 values (2,55);
insert into t3 values (1,12);
explain analyze select * from t1 left join t2 on = left join t3 on =;
              QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
 Hash Right Join (cost=89.82..337.92 rows=17877 width=540) (actual time=0.053..0.059 rows=3 loops=1)
 Hash Cond: ( = )
 -> Seq Scan on t3 (cost=0.00..32.60 rows=2260 width=8) (actual time=0.002..0.002 rows=3 loops=1)
 -> Hash (cost=70.05..70.05 rows=1582 width=532) (actual time=0.042..0.043 rows=3 loops=1)
   Buckets: 2048 Batches: 1 Memory Usage: 17kB
   -> Hash Right Join (cost=13.15..70.05 rows=1582 width=532) (actual time=0.034..0.039 rows=3 loops=1)
    Hash Cond: ( = )
    -> Seq Scan on t2 (cost=0.00..32.60 rows=2260 width=8) (actual time=0.002..0.002 rows=3 loops=1)
    -> Hash (cost=11.40..11.40 rows=140 width=524) (actual time=0.017..0.017 rows=3 loops=1)
      Buckets: 1024 Batches: 1 Memory Usage: 9kB
      -> Seq Scan on t1 (cost=0.00..11.40 rows=140 width=524) (actual time=0.010..0.011 rows=3 loops=1)
 Planning time: 0.154 ms
 Execution time: 0.133 ms

Create an index

create index idx_t1_id on t1(id);
create index idx_t2_id on t2(id);
create index idx_t3_id on t3(id);
explain analyze select * from t1 left join t2 on = left join t3 on =;
             QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Hash Left Join (cost=2.14..3.25 rows=3 width=540) (actual time=0.045..0.047 rows=3 loops=1)
 Hash Cond: ( = )
 -> Hash Left Join (cost=1.07..2.14 rows=3 width=532) (actual time=0.030..0.032 rows=3 loops=1)
   Hash Cond: ( = )
   -> Seq Scan on t1 (cost=0.00..1.03 rows=3 width=524) (actual time=0.005..0.006 rows=3 loops=1)
   -> Hash (cost=1.03..1.03 rows=3 width=8) (actual time=0.007..0.007 rows=3 loops=1)
    Buckets: 1024 Batches: 1 Memory Usage: 9kB
    -> Seq Scan on t2 (cost=0.00..1.03 rows=3 width=8) (actual time=0.002..0.003 rows=3 loops=1)
 -> Hash (cost=1.03..1.03 rows=3 width=8) (actual time=0.005..0.005 rows=3 loops=1)
   Buckets: 1024 Batches: 1 Memory Usage: 9kB
   -> Seq Scan on t3 (cost=0.00..1.03 rows=3 width=8) (actual time=0.002..0.002 rows=3 loops=1)
 Planning time: 0.305 ms
 Execution time: 0.128 ms

4.2 Forced index scan

/*+ indexscan(t1 idx_d)
/*+ indexscan(t1 idx_t1_id)
explain (analyze,buffers) select * from t1 where id=2;
           QUERY PLAN
----------------------------------------------------------------------------------------------
 Seq Scan on t1 (cost=0.00..1.04 rows=1 width=524) (actual time=0.011..0.013 rows=1 loops=1)
 Filter: (id = 2)
 Rows Removed by Filter: 2
 Buffers: shared hit=1
 Planning time: 0.058 ms
 Execution time: 0.028 ms
explain (analyze,buffers) /*+ indexscan(t1) */select * from t1 where id=2;
             QUERY PLAN
----------------------------------------------------------------------------------------------------------------
 Index Scan using idx_t1_id on t1 (cost=0.13..8.15 rows=1 width=524) (actual time=0.044..0.046 rows=1 loops=1)
 Index Cond: (id = 2)
 Buffers: shared hit=1 read=1
 Planning time: 0.145 ms
 Execution time: 0.072 ms
explain (analyze,buffers) /*+ indexscan(t1 idx_t1_id) */select * from t1 where id=2;
             QUERY PLAN
----------------------------------------------------------------------------------------------------------------
 Index Scan using idx_t1_id on t1 (cost=0.13..8.15 rows=1 width=524) (actual time=0.016..0.017 rows=1 loops=1)
 Index Cond: (id = 2)
 Buffers: shared hit=2
 Planning time: 0.079 ms
 Execution time: 0.035 ms

4.3 Forced multi-condition combination

/*+ indexscan(t2) indexscan(t1 idx_t1_id) */
/*+ seqscan(t2) indexscan(t1 idx_t1_id) */
explain analyze SELECT * FROM t1 JOIN t2 ON ( = );
            QUERY PLAN
--------------------------------------------------------------------------------------------------------
 Hash Join (cost=1.07..2.14 rows=3 width=532) (actual time=0.018..0.020 rows=3 loops=1)
 Hash Cond: ( = )
 -> Seq Scan on t1 (cost=0.00..1.03 rows=3 width=524) (actual time=0.006..0.007 rows=3 loops=1)
 -> Hash (cost=1.03..1.03 rows=3 width=8) (actual time=0.005..0.005 rows=3 loops=1)
   Buckets: 1024 Batches: 1 Memory Usage: 9kB
   -> Seq Scan on t2 (cost=0.00..1.03 rows=3 width=8) (actual time=0.001..0.003 rows=3 loops=1)
 Planning time: 0.114 ms
 Execution time: 0.055 ms
(8 rows)

Combining two conditions to go indexscan

/*+ indexscan(t2) indexscan(t1 idx_t1_id) */explain analyze SELECT * FROM t1 JOIN t2 ON ( = );
              QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Merge Join (cost=0.26..24.40 rows=3 width=532) (actual time=0.047..0.053 rows=3 loops=1)
 Merge Cond: ( = )
 -> Index Scan using idx_t1_id on t1 (cost=0.13..12.18 rows=3 width=524) (actual time=0.014..0.015 rows=3 loops=1)
 -> Index Scan using idx_t2_id on t2 (cost=0.13..12.18 rows=3 width=8) (actual time=0.026..0.028 rows=3 loops=1)

Combine two conditions to indexscan+seqscan

/*+ seqscan(t2) indexscan(t1 idx_t1_id) */explain analyze SELECT * FROM t1 JOIN t2 ON ( = );
              QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Nested Loop (cost=0.13..13.35 rows=3 width=532) (actual time=0.025..0.032 rows=3 loops=1)
 Join Filter: ( = )
 Rows Removed by Join Filter: 6
 -> Index Scan using idx_t1_id on t1 (cost=0.13..12.18 rows=3 width=524) (actual time=0.016..0.018 rows=3 loops=1)
 -> Materialize (cost=0.00..1.04 rows=3 width=8) (actual time=0.002..0.003 rows=3 loops=3)
   -> Seq Scan on t2 (cost=0.00..1.03 rows=3 width=8) (actual time=0.004..0.005 rows=3 loops=1)

4.4 Forced to specify the join method

/*+ NestLoop(t1 t2) MergeJoin(t1 t2 t3) Leading(t1 t2 t3) */
/*+ NestLoop(t1 t2 t3) MergeJoin(t2 t3) Leading(t1 (t2 t3)) */
explain analyze select * from t1 left join t2 on = left join t3 on =;
             QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Hash Left Join (cost=2.14..3.25 rows=3 width=540) (actual time=0.053..0.056 rows=3 loops=1)
 Hash Cond: ( = )
 -> Hash Left Join (cost=1.07..2.14 rows=3 width=532) (actual time=0.036..0.038 rows=3 loops=1)
   Hash Cond: ( = )
   -> Seq Scan on t1 (cost=0.00..1.03 rows=3 width=524) (actual time=0.007..0.007 rows=3 loops=1)
   -> Hash (cost=1.03..1.03 rows=3 width=8) (actual time=0.009..0.009 rows=3 loops=1)
    Buckets: 1024 Batches: 1 Memory Usage: 9kB
    -> Seq Scan on t2 (cost=0.00..1.03 rows=3 width=8) (actual time=0.002..0.003 rows=3 loops=1)
 -> Hash (cost=1.03..1.03 rows=3 width=8) (actual time=0.006..0.006 rows=3 loops=1)
   Buckets: 1024 Batches: 1 Memory Usage: 9kB
   -> Seq Scan on t3 (cost=0.00..1.03 rows=3 width=8) (actual time=0.002..0.003 rows=3 loops=1)

Forced loop nested connection

/*+ NestLoop(t1 t2) MergeJoin(t1 t2 t3) Leading(t1 t2 t3) */
explain analyze select * from t1 left join t2 on = left join t3 on =;
              QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Merge Left Join (cost=3.28..3.34 rows=3 width=540) (actual time=0.093..0.096 rows=3 loops=1)
 Merge Cond: ( = )
 -> Sort (cost=2.23..2.23 rows=3 width=532) (actual time=0.077..0.078 rows=3 loops=1)
   Sort Key: 
   Sort Method: quicksort Memory: 25kB
   -> Nested Loop Left Join (cost=0.00..2.20 rows=3 width=532) (actual time=0.015..0.020 rows=3 loops=1)
    Join Filter: ( = )
    Rows Removed by Join Filter: 6
    -> Seq Scan on t1 (cost=0.00..1.03 rows=3 width=524) (actual time=0.005..0.005 rows=3 loops=1)
    -> Materialize (cost=0.00..1.04 rows=3 width=8) (actual time=0.002..0.003 rows=3 loops=3)
      -> Seq Scan on t2 (cost=0.00..1.03 rows=3 width=8) (actual time=0.002..0.003 rows=3 loops=1)
 -> Sort (cost=1.05..1.06 rows=3 width=8) (actual time=0.012..0.013 rows=3 loops=1)
   Sort Key: 
   Sort Method: quicksort Memory: 25kB
   -> Seq Scan on t3 (cost=0.00..1.03 rows=3 width=8) (actual time=0.002..0.003 rows=3 loops=1)

Control the connection order

/*+ NestLoop(t1 t2 t3) MergeJoin(t2 t3) Leading(t1 (t2 t3)) */
explain analyze select * from t1 left join t2 on = left join t3 on =;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Nested Loop Left Join (cost=1.07..3.31 rows=3 width=540) (actual time=0.036..0.041 rows=3 loops=1)
 Join Filter: ( = )
 Rows Removed by Join Filter: 6
 -> Hash Left Join (cost=1.07..2.14 rows=3 width=532) (actual time=0.030..0.032 rows=3 loops=1)
   Hash Cond: ( = )
   -> Seq Scan on t1 (cost=0.00..1.03 rows=3 width=524) (actual time=0.008..0.009 rows=3 loops=1)
   -> Hash (cost=1.03..1.03 rows=3 width=8) (actual time=0.007..0.007 rows=3 loops=1)
    Buckets: 1024 Batches: 1 Memory Usage: 9kB
    -> Seq Scan on t2 (cost=0.00..1.03 rows=3 width=8) (actual time=0.002..0.004 rows=3 loops=1)
 -> Materialize (cost=0.00..1.04 rows=3 width=8) (actual time=0.001..0.002 rows=3 loops=3)
   -> Seq Scan on t3 (cost=0.00..1.03 rows=3 width=8) (actual time=0.002..0.003 rows=3 loops=1)

4.5 Control the cost of a single SQL

/*+ set(seq_page_cost 20.0) seqscan(t1) */
/*+ set(seq_page_cost 20.0) seqscan(t1) */explain analyze select * from t1 where id > 1;
           QUERY PLAN
-----------------------------------------------------------------------------------------------
 Seq Scan on t1 (cost=0.00..20.04 rows=1 width=524) (actual time=0.011..0.013 rows=2 loops=1)
 Filter: (id > 1)
 Rows Removed by Filter: 1

set seq_page_cost 200, note that the cost below has become 200.04

/*+ set(seq_page_cost 200.0) seqscan(t1) */explain analyze select * from t1 where id > 1;
           QUERY PLAN
------------------------------------------------------------------------------------------------
 Seq Scan on t1 (cost=0.00..200.04 rows=1 width=524) (actual time=0.010..0.011 rows=2 loops=1)
 Filter: (id > 1)
 Rows Removed by Filter: 1

The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.