In PostgreSQL, the planner (optimizer) looks up relevant statistics based on the SQL query, selects the fastest and lowest-cost method, and creates an execution plan. However, this may not always be the optimal plan, especially when the statistics are stale after a large number of update queries or if statistics change too often.
Furthermore, in core business systems, stability is critical and is often prioritized over performance. In such cases, you would want to avoid ever-changing execution plans and rather take control by setting consistent plans.
Hint phrases can be set in queries which explicitly specify access methods like table scans and joins. This forces the planner (optimizer) to output stable execution plans.
This article will describe how to tune your database system by leveraging hint phrases of pg_hint_plan, one of the tools available for PostgreSQL.
pg_hint_plan is a tool that allows the user to control execution plans in PostgreSQL by setting hint phrases. pg_hint_plan runs on Linux, Windows, and Solaris. The types of hint phrases that can be specified in pg_hint_plan are as follows.
Reference
Let's see step by step how to set hint phrases using pg_hint_plan. This example uses PostgreSQL 11.1 and pg_hint_plan 11.1.3.2.
To use pg_hint_plan, you need to obtain it from GitHub and install it, and then make the following preparations.
$ psql -d mydb -c "CREATE EXTENSION pg_hint_plan;"
shared_preload_libraries = 'pg_hint_plan'
This section describes a tuning example that uses hint phrases to specify the search process of tables with an index. It assumes that the emp and dept tables already exist.
mydb=# \d emp;
Table "public.emp"
Column | Type | Collation | Nullable | Default
-------+---------+-----------+----------+--------
empno | integer | | not null |
name | text | | |
age | integer | | |
deptno | integer | | |
salary | integer | | |
Index
"emp_pkey" PRIMARY KEY, btree (empno) 1
"emp_age_index" btree (age) 2
mydb=# \d dept;
Table "public.dept"
Column | Type | Collation | Nullable | Default
-------+---------+-----------+----------+--------
deptno | integer | | |
name | text | | |
Index
"dept_deptno_index" UNIQUE, btree (deptno) 3
mydb=# SELECT COUNT(*) FROM emp;
count
-------
2000
(1 row)
mydb=# SELECT COUNT(*) FROM dept;
count
------
30
(1 row)
mydb=# ANALYZE;
ANALYZE
mydb=# EXPLAIN ANALYZE WITH age30 as (SELECT * FROM emp WHERE age BETWEEN 30 AND 39)
SELECT * FROM age30, dept WHERE age30.deptno = dept.deptno ;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------
Hash Join (cost=43.49..58.34 rows=98 width=58) (actual time=0.260..5.645 rows=656 loops=1)
Hash Cond: (age30.deptno = dept.deptno)
CTE age30
-> Bitmap Heap Scan on emp 1(cost=18.99..41.82 rows=655 width=22)
(actual time=0.064..1.158 rows=656 a loops=1)
Recheck Cond: ((age >= 30) AND (age <= 39))
Heap Blocks: exact=8
-> Bitmap Index Scan on emp_age_index (cost=0.00..18.83 rows=655 width=0)
(actual time=0.049..0.051 rows=656 a loops=1)
Index Cond: ((age >= 30) AND (age <= 39))
-> CIE Scan on age30 (cost=0.00..13.10 rows=655 width=48) (actual time=0.070..3.268 rows=656 loops=1)
-> Hash (cost=1.30..1.30 rows=30 width=10) (actual time=0.155..0.156 rows=30 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 10kB
-> Seq Scan on dept 2(cost=0.00..1.30 rows=30 width=10) (actual time=0.009..0.058 rows=30 b loops=1)
Planning Time: 0.650 ms 2
Execution Time: 6.842 ms
(14 rows)
The following are typical scanning methods using indexes. The circled numbers indicate the order of access to the index and the table.
Scanning method | Description |
Index Scan |
Randomly accesses the index and the table alternately. This is an effective method when you want to access specific data or when the number of items to be retrieved using WHERE is small. |
Bitmap Scan |
The candidate rows from the indexes are bitmapped in memory, and only the candidate rows from tables are acquired. After making a bitmap, the table is accessed sequentially, skipping non-candidate rows. This is an effective method when the number of items to be retrieved is moderate. |
Index only scan |
Only accesses the indexes and returns the result for the applicable index. This is a useful method when you only need information for the indexed data. |
mydb=# EXPLAIN ANALYZE WITH /*+ IndexScan (emp emp_age_index) */
age30 as (SELECT * FROM emp WHERE age BETWEEN 30 AND 39)
SELECT * FROM age30, dept WHERE age30.deptno = dept.deptno;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Hash Join (cost=46.65..61.50 rows=98 width=58) (actual time=0.240..6.309 rows=656 loops=1)
Hash Cond: (age30.deptno = dept.deptno)
CTE age30
-> Index Scan using emp_age_index on emp 1 (cost=0.28..44.98 rows=655 width=22)
(actual time=0.031..1.363 rows=656 loops=1)
Index Cond: ((age >= 30) AND (age <= 39))
-> CTE Scan on age30 (cost=0.00..13.10 rows=655 width=48) (actual time=0.038..3.766 rows=656 loops=1)
-> Hash (cost=1.30..1.30 rows=30 width=10) (actual time=0.181..0.183 rows=30 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 10kB
-> Seq Scan on dept (cost=0.00..1.30 rows=30 width=10) (actual time=0.011..0.076 rows=30 loops=1)
Planning Time: 0.406 ms
Execution Time: 7.517 ms
(11 rows)
The methods for specifying the hint phrase in pg_hint_plan are as follows:
The types of hint phrases that can be specified in pg_hint_plan and typical hint phrase formats are shown below.
If the table has a different name in the SQL statement, specify the alias in the hint phrase.
You can specify multiple hint phrases in one query. An example is shown below.
WITH /*+ SeqScan (ta) IndexScan (tb) */
over100 AS (SELECT empno FROM emp ta WHERE salary > 1000000)
SELECT * FROM emp tb, over100 WHERE tb.empno = over100.empno AND tb.age < 30;
Name the emp table differently (for example "ta" and "tb"), then specify hint phrases separately.
Fujitsu Enterprise Postgres has been bundling pg_hint_plan since version 9.5. Because pg_hint_plan is bundled with the product, it is not necessary to acquire it separately.
pg_hint_plan is a convenient feature that allows the user to control execution plans, but the following points should be noted.
pg_hint_plan is effective in tuning one-time SQL with fixed business requirements, verifying how the execution plan works, or stabilizing performance. It is a powerful tool when employed accordingly to each scenario.
We hope this article helped you to understand the features and the use of pg_hint_plan as one of the means of performance tuning. Remember to always select the method that best suits your system and business requirements.
Fujitsu Enterprise Postgres is designed for developers, data architects, and DBAs seeking the efficiency of a leading open source based database technology with the enhanced security and performance features plus support required in a serious business environment.
Read our most recent articles regarding all aspects of PostgreSQL and Fujitsu Enterprise Postgres.