一、环境

名称 版本
操作系统 Centos7.9
Postgresql 14.4
pg_hint_plan 1.4

二、下载链接

pg_hint_plan下载地址

三、pg_hint_plan用途

当判断数据库优化器生成的执行计划不是最优的时候,可以在不修改Sql的情况下(例如:等价改写、非等价改写)通过添加Hint,来影响执行计划的生成。

四、安装pg_hint_plan

Postgresql源码安装可以参考之前的博客分享:
《Postgresql学习01-PG14.2-Linux平台源码编译安装和配置开机自启》

[root@node0 pkg]# unzip pg_hint_plan-master.zip[root@node0 pkg]# source /home/postgres/.bashrc[root@node0 pkg]# cd pg_hint_plan-master/[root@node0 pg_hint_plan-master]# ll
总用量 280
-rw-r--r-- 1 root root   1593 1月  19 14:12 COPYRIGHT
-rw-r--r-- 1 root root   1295 1月  19 14:12 COPYRIGHT.postgresql
-rw-r--r-- 1 root root  55461 1月  19 14:12 core.c
drwxr-xr-x 2 root root     22 1月  19 14:12 data
drwxr-xr-x 2 root root    123 1月  19 14:12 doc
drwxr-xr-x 2 root root    297 1月  19 14:12 expected
drwxr-xr-x 2 root root     27 1月  19 14:12 input
-rw-r--r-- 1 root root   2033 1月  19 14:12 Makefile
-rw-r--r-- 1 root root  12313 1月  19 14:12 make_join_rel.c
-rw-r--r-- 1 root root    521 1月  19 14:12 normalize_query.h
drwxr-xr-x 2 root root     27 1月  19 14:12 output
-rw-r--r-- 1 root root    683 1月  19 14:12 pg_hint_plan--1.4.sql
-rw-r--r-- 1 root root 124735 1月  19 14:12 pg_hint_plan.c
-rw-r--r-- 1 root root    102 1月  19 14:12 pg_hint_plan.control
-rw-r--r-- 1 root root   8342 1月  19 14:12 pg_stat_statements.c
-rw-r--r-- 1 root root  35842 1月  19 14:12 README.md
drwxr-xr-x 2 root root     33 1月  19 14:12 SPECS
drwxr-xr-x 2 root root    314 1月  19 14:12 sql
-rwxr-xr-x 1 root root   9109 1月  19 14:12 update_copied_funcs.pl[root@node0 pg_hint_plan-master]# make
gcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wf
ormat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -fPIC -I. -I./ -I/opt/pg14-4/include/postgresql/server -I/opt/pg14-4/include/postgresql/internal  -D_GNU_SOURCE   -c -o pg_hint_plan.o pg_hint_plan.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wf
ormat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -fPIC pg_hint_plan.o -L/opt/pg14-4/lib   -Wl,--as-needed -Wl,-rpath,'/opt/pg14-4/lib',--enable-new-dtags  -shared -o pg_hint_plan.so[root@node0 pg_hint_plan-master]# make install
/usr/bin/mkdir -p '/opt/pg14-4/share/postgresql/extension'
/usr/bin/mkdir -p '/opt/pg14-4/share/postgresql/extension'
/usr/bin/mkdir -p '/opt/pg14-4/lib/postgresql'
/usr/bin/install -c -m 644 .//pg_hint_plan.control '/opt/pg14-4/share/postgresql/extension/'
/usr/bin/install -c -m 644 .//pg_hint_plan--*.sql  '/opt/pg14-4/share/postgresql/extension/'
/usr/bin/install -c -m 755  pg_hint_plan.so '/opt/pg14-4/lib/postgresql/'[root@node0 pg_hint_plan-master]# systemctl restart pg14-4.service[root@node0 pg14-4]# su - postgres
上一次登录:一 7月 18 15:19:03 CST 2022[postgres@node0 ~]$ psql
psql (14.4)
Type "help" for help.postgres=# create extension pg_hint_plan;
CREATE EXTENSIONpostgres=# load 'pg_hint_plan';
LOADpostgres=# select * from pg_available_extensions where name = 'pg_hint_plan';name     | default_version | installed_version | comment
--------------+-----------------+-------------------+---------pg_hint_plan | 1.4             | 1.4               |
(1 row)postgres=# \d hint_plan.hintsTable "hint_plan.hints"Column       |  Type   | Collation | Nullable |                   Default
-------------------+---------+-----------+----------+---------------------------------------------id                | integer |           | not null | nextval('hint_plan.hints_id_seq'::regclass)norm_query_string | text    |           | not null | application_name  | text    |           | not null | hints             | text    |           | not null |
Indexes:"hints_pkey" PRIMARY KEY, btree (id)"hints_norm_and_app" UNIQUE, btree (norm_query_string, application_name)

1、注意点

(1)必须执行create extension pg_hint_plan;,不然找不到hint_plan.hints表。

(2)上面的load ‘pg_hint_plan’;是临时生效,如果重启数据库,就不生效了,建议用下面这个方法可以永久生效,修改postgresql.conf中的shared_preload_libraries参数,修改完记得重启数据库。

shared_preload_libraries = 'pg_hint_plan'

五、支持的Hint介绍

1、扫描方法

Hint 描述
SeqScan(table) 强制对表进行顺序扫描
TidScan(table) 强制对表进行TID扫描。
IndexScan(table[ index…]) 强制对表进行索引扫描。限制指定索引(如果有)。
IndexOnlyScan(table[ index…]) 强制仅对表进行索引扫描。如果有,则限制为指定索引。如果索引扫描不可用,可以使用索引扫描。可用于PostgreSQL 9.2及更高版本。
BitmapScan(table[ index…]) 强制对表进行位图扫描。如果有,恢复到指定的索引。
IndexScanRegexp(table[ POSIX Regexp…]) IndexOnlyScanRegexp(table[ POSIX Regexp…]) BitmapScanRegexp(table[ POSIX Regexp…]) 强制索引扫描或仅索引扫描(适用于PostgreSQL 9.2及以后版本)或位图扫描表。限制为匹配指定POSIX正则表达式模式的索引
NoSeqScan(table) 强制不对表进行顺序扫描。
NoTidScan(table) 强制不对表进行TID扫描。
NoIndexScan(table) 强制对表不做索引扫描,只做索引扫描(适用于PostgreSQL 9.2及以后版本)。
NoIndexOnlyScan(table) 强制不在表上只做索引扫描。可用于PostgreSQL 9.2及更高版本。
NoBitmapScan(table) 强制不对表进行位图扫描。

2、连接方法

Hint 描述
NestLoop(table table[ table…]) 强制联接的嵌套循环由指定的表组成。
HashJoin(table table[ table…]) 强制由指定表组成的连接的散列连接。
MergeJoin(table table[ table…])) 强制合并联接,因为联接由指定的表组成。
NoNestLoop(table table[ table…])) 对于由指定表组成的连接,强制不执行嵌套循环。
NoHashJoin(table table[ table…])) 强制不执行散列连接,因为连接由指定的表组成。
NoMergeJoin(table table[ table…])) 强制不进行合并连接,因为连接由指定的表组成。

3、连接顺序

Hint 描述
Leading(table table[ table…]) ) 强制指定的连接顺序。
Leading(<join pair>) 力按照指定的顺序和方向连接。连接对是一对用括号括起来的表和/或其他连接对,它们可以构成嵌套结构。

4、行号修正

Hint 描述
Rows(table table[ table…] correction)) ) 纠正由指定表组成的连接的结果的行号。可用的校正方法有绝对(#)、加法(+)、减法(-)和乘法(*)。应该是strtod()可以读取的字符串。

5、并行查询的配置

Hint 描述
Parallel(table <# of workers> [soft|hard]) 强制或禁止指定表的并行执行。<# of worker >是期望的并行工作者数量,其中0表示抑制并行执行。如果第三个参数是软参数(默认值),它只更改max_parallel_workers_per_gather,并将其他所有工作留给规划器。Hard意味着强制执行指定的工人数量。

6、GUC

Hint 描述
Set(GUC-param value) 将规划器运行时的GUC参数设置为该值。

六、Sql执行计划操作符

操作符 描述
Seq Scan 顺序扫描也就是全表扫描
Index Scan 索引扫描
Bitmap Index Scan 位图索引扫描
Bitmap Heap Scan 位图堆扫描
Subquery Scan 子查询
Tid Scan 行号扫描
Function Scan 函数扫描
Nested Loop 嵌套循环连接
Merge Join 归并连接
Hash Join 哈希连接
Sort 排序运算
Unique 唯一运算
Limit 限制返回的行数
Aggregate 聚合运算
Group 分组运算
Append 追加运算,union出现
Materialize 子查询
SetOp 交集INTERCECT、不包含EXCEPT出现

七、执行计划代价估算

1、准备测试环境

czg=# create table tenk1(id int,num int,name varchar(100),sex int);
CREATE TABLEczg=# insert into tenk1  select generate_series(1,20902),generate_series(1,20902),chr(generate_series(19968,40869)),random()::int;
INSERT 0 20902czg=# create table tenk2(id int,num int,name varchar(100),sex int);
CREATE TABLEczg=# insert into tenk2  select generate_series(1,20902),generate_series(1,20902),chr(generate_series(19968,40869)),random()::int;
INSERT 0 20902czg=# select * from tenk1 limit 10;id | num | name | sex
----+-----+------+-----1 |   1 | 一   |   12 |   2 | 丁   |   03 |   3 | 丂   |   04 |   4 | 七   |   15 |   5 | 丄   |   06 |   6 | 丅   |   17 |   7 | 丆   |   18 |   8 | 万   |   19 |   9 | 丈   |   010 |  10 | 三   |   0
(10 rows)

2、收集统计信息并查看

czg=# \x
Expanded display is on.czg=# ANALYZE VERBOSE TENK1;
INFO:  analyzing "public.tenk1"
INFO:  "tenk1": scanned 113 of 113 pages, containing 20902 live rows and 0 dead rows; 20902 rows in sample, 20902 estimated total rows
ANALYZEczg=# ANALYZE VERBOSE TENK2;
INFO:  analyzing "public.tenk2"
INFO:  "tenk2": scanned 113 of 113 pages, containing 20902 live rows and 0 dead rows; 20902 rows in sample, 20902 estimated total rows
ANALYZEczg=# select * from pg_stats where tablename = 'tenk1';
-[ RECORD 1 ]----------+-------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------
schemaname             | public
tablename              | tenk1
attname                | name
inherited              | f
null_frac              | 0
avg_width              | 4
n_distinct             | -1
most_common_vals       |
most_common_freqs      |
histogram_bounds       | {丆,炿,飊,蔜,鎊,夶,墂,桲,愺,蕆,嚫,叱,絀,鷀,髊,蜑,掋,苵,妬,莪,匥,蚡,縛,竿,舸,淈,敮,寒,佫,謼,镮,晦,积,瀱,笺,薑,嵥,聙,踙,屫
,瞰,郀,焜,壏,踜,蒞,聊,陵,髏,倫,麦,痗,緬,縸,詉,鎳,輫,匹,胓,脐,虔,锲,紌,悫,氄,繅,赏,冼,授,丝,膆,孡,洮,齠,駼,紈,踓,诬,稀,夏,鄕,卨,綇,袨,珚,眏,偞,义,
絪,巊,淤,籞,跃,醩,颭,蒖,址,譸,僎,耔,阼}
correlation            | 0.016811712
most_common_elems      |
most_common_elem_freqs |
elem_count_histogram   |
-[ RECORD 2 ]----------+-------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------
schemaname             | public
tablename              | tenk1
attname                | sex
inherited              | f
null_frac              | 0
avg_width              | 4
n_distinct             | 2
most_common_vals       | {0,1}
most_common_freqs      | {0.5002392,0.49976078}
histogram_bounds       |
correlation            | 0.493415
most_common_elems      |
most_common_elem_freqs |
elem_count_histogram   |
-[ RECORD 3 ]----------+-------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------
schemaname             | public
tablename              | tenk1
attname                | id
inherited              | f
null_frac              | 0
avg_width              | 4
n_distinct             | -1
most_common_vals       |
most_common_freqs      |
histogram_bounds       | {1,210,419,628,837,1046,1255,1464,1673,1882,2091,2300,2509,2718,2927,3136,3345,3554,3763,3972,4181,4390,4599,4808,5017,5
226,5435,5644,5853,6062,6271,6480,6689,6898,7107,7316,7525,7734,7943,8152,8361,8570,8779,8988,9197,9406,9615,9824,10033,10242,10451,10660,10869,1
1078,11287,11496,11705,11914,12123,12332,12541,12750,12959,13168,13377,13586,13795,14004,14213,14422,14631,14840,15049,15258,15467,15676,15885,16
094,16303,16512,16721,16930,17139,17348,17557,17766,17975,18184,18393,18602,18811,19020,19229,19438,19647,19856,20065,20274,20483,20692,20902}
correlation            | 1
most_common_elems      |
most_common_elem_freqs |
elem_count_histogram   |
-[ RECORD 4 ]----------+-------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------
schemaname             | public
tablename              | tenk1
attname                | num
inherited              | f
null_frac              | 0
avg_width              | 4
n_distinct             | -1
most_common_vals       |
most_common_freqs      |
histogram_bounds       | {1,210,419,628,837,1046,1255,1464,1673,1882,2091,2300,2509,2718,2927,3136,3345,3554,3763,3972,4181,4390,4599,4808,5017,5
226,5435,5644,5853,6062,6271,6480,6689,6898,7107,7316,7525,7734,7943,8152,8361,8570,8779,8988,9197,9406,9615,9824,10033,10242,10451,10660,10869,1
1078,11287,11496,11705,11914,12123,12332,12541,12750,12959,13168,13377,13586,13795,14004,14213,14422,14631,14840,15049,15258,15467,15676,15885,16
094,16303,16512,16721,16930,17139,17348,17557,17766,17975,18184,18393,18602,18811,19020,19229,19438,19647,19856,20065,20274,20483,20692,20902}
correlation            | 1
most_common_elems      |
most_common_elem_freqs |
elem_count_histogram   | czg=# SELECT relpages, reltuples FROM pg_class WHERE relname = 'tenk1';
-[ RECORD 1 ]----
relpages  | 113
reltuples | 20902

3、查看系统参数

czg=# \x
Expanded display is off.
czg=# select name,setting from pg_settings where name like '%_cost';name           | setting
-------------------------+---------cpu_index_tuple_cost    | 0.005cpu_operator_cost       | 0.0025cpu_tuple_cost          | 0.01jit_above_cost          | 100000jit_inline_above_cost   | 500000jit_optimize_above_cost | 500000parallel_setup_cost     | 1000parallel_tuple_cost     | 0.1random_page_cost        | 4seq_page_cost           | 1
(10 rows)

4、Sql执行计划

czg=# EXPLAIN SELECT * FROM tenk1;QUERY PLAN
------------------------------------------------------------Seq Scan on tenk1  (cost=0.00..322.02 rows=20902 width=16)
(1 row)

5、代价估算公式

cost = (disk pages read * seq_page_cost) + (rows scanned * cpu_tuple_cost)= 113 * 1.0 + 20902 * 0.01= 113 + 209.02= 322.02

估算结果和和执行计划中的一样。

八、如何看执行计划

1、两表连接查询

czg=# set enable_hashjoin = off;
SETczg=# insert into tenk1 values(1,1,'一',1);
INSERT 0 1czg=# explain (analyze,verbose,buffers) select * from tenk1 t1, tenk2 t2 where t1.id = t2.id and t1.name = '一';QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------Nested Loop  (cost=0.00..957.57 rows=1 width=32) (actual time=0.027..17.943 rows=2 loops=1)Output: t1.id, t1.num, t1.name, t1.sex, t2.id, t2.num, t2.name, t2.sexJoin Filter: (t1.id = t2.id)Rows Removed by Join Filter: 41802Buffers: shared hit=339->  Seq Scan on public.tenk1 t1  (cost=0.00..374.28 rows=1 width=16) (actual time=0.016..4.690 rows=2 loops=1)Output: t1.id, t1.num, t1.name, t1.sexFilter: ((t1.name)::text = '一'::text)Rows Removed by Filter: 20901Buffers: shared hit=113->  Seq Scan on public.tenk2 t2  (cost=0.00..322.02 rows=20902 width=16) (actual time=0.005..2.774 rows=20902 loops=2)Output: t2.id, t2.num, t2.name, t2.sexBuffers: shared hit=226Planning Time: 0.157 msExecution Time: 17.974 ms
(15 rows)

操作符执行顺序为:
(1)先全表扫描tenk1表,根据name = '一’进行过滤,tenk1作为驱动表,驱动表滤出2条数据,过滤掉了20901条数据。

   ->  Seq Scan on public.tenk1 t1  (cost=0.00..374.28 rows=1 width=16) (actual time=0.016..4.690 rows=2 loops=1)Output: t1.id, t1.num, t1.name, t1.sexFilter: ((t1.name)::text = '一'::text)Rows Removed by Filter: 20901Buffers: shared hit=113

(2)再全表扫描tenk2表,tenk2作为被驱动表,loops=2,表示执行两次,总结为:驱动表滤出多少条数据,被驱动表执行多少次。

   ->  Seq Scan on public.tenk2 t2  (cost=0.00..322.02 rows=20902 width=16) (actual time=0.005..2.774 rows=20902 loops=2)Output: t2.id, t2.num, t2.name, t2.sexBuffers: shared hit=226

(3)最后将两个表根据等值条件t1.id = t2.id进行嵌套循环连接。

 Nested Loop  (cost=0.00..957.57 rows=1 width=32) (actual time=0.027..17.943 rows=2 loops=1)Output: t1.id, t1.num, t1.name, t1.sex, t2.id, t2.num, t2.name, t2.sexJoin Filter: (t1.id = t2.id)Rows Removed by Join Filter: 41802Buffers: shared hit=339

2、多表连接查询

(1)准备测试环境

czg=# CREATE TABLE FLATING_T1(A1 INT,A2 INT);
CREATE TABLEczg=# CREATE TABLE FLATING_T2(B1 INT,B2 INT);
CREATE TABLEczg=# CREATE TABLE FLATING_T3(C1 INT,C2 INT);
CREATE TABLEczg=# INSERT INTO FLATING_T1 SELECT generate_series(1,10),generate_series(1,10);
INSERT 0 10czg=# INSERT INTO FLATING_T2 SELECT generate_series(1,100000),generate_series(1,100000);
INSERT 0 100000czg=# INSERT INTO FLATING_T3 SELECT generate_series(1,100000),generate_series(1,100000);
INSERT 0 100000czg=# ANALYZE VERBOSE FLATING_T1;
INFO:  analyzing "public.flating_t1"
INFO:  "flating_t1": scanned 1 of 1 pages, containing 10 live rows and 0 dead rows; 10 rows in sample, 10 estimated total rows
ANALYZEczg=# ANALYZE VERBOSE FLATING_T2;
INFO:  analyzing "public.flating_t2"
INFO:  "flating_t2": scanned 443 of 443 pages, containing 100000 live rows and 0 dead rows; 30000 rows in sample, 100000 estimated total rows
ANALYZEczg=# ANALYZE VERBOSE FLATING_T3;
INFO:  analyzing "public.flating_t3"
INFO:  "flating_t3": scanned 443 of 443 pages, containing 100000 live rows and 0 dead rows; 30000 rows in sample, 100000 estimated total rows
ANALYZE

(2)小实验

czg=# EXPLAIN SELECT COUNT(*) FROM FLATING_T1 LEFT JOIN FLATING_T2 CROSS JOIN FLATING_T3 ON A1=B1;QUERY PLAN
------------------------------------------------------------------------------------------Aggregate  (cost=162515637.22..162515637.23 rows=1 width=8)->  Hash Right Join  (cost=1.23..162513137.22 rows=1000000 width=0)Hash Cond: (flating_t2.b1 = flating_t1.a1)->  Nested Loop  (cost=0.00..125003136.00 rows=10000000000 width=4)->  Seq Scan on flating_t2  (cost=0.00..1443.00 rows=100000 width=4)->  Materialize  (cost=0.00..1943.00 rows=100000 width=0)->  Seq Scan on flating_t3  (cost=0.00..1443.00 rows=100000 width=0)->  Hash  (cost=1.10..1.10 rows=10 width=4)->  Seq Scan on flating_t1  (cost=0.00..1.10 rows=10 width=4)
(9 rows)

操作符执行顺序为:
flating_t3 》Materialize 》flating_t2 》Nested Loop 》 flating_t1 》Hash 》Hash Right Join 》 Aggregate

flating_t3有10万条数,flating_t2有10万条数,进行交叉连接(也就是笛卡尔积连接)返回的结果是100亿,耗时在这里了,之后再和 flating_t1的10条数据进行左外连接。

(3)如何优化呢

等价改写的方式就可以出来啦。
我们先让 flating_t1和flating_t2进行左外连接得出10条数据,再和flating_t3进行交叉连接,得出最终的100万条数据。

czg=# explain
czg-# with a as (select * from FLATING_T1 LEFT JOIN FLATING_T2 ON A1=B1)
czg-# SELECT COUNT(*) FROM a,FLATING_T3;QUERY PLAN
------------------------------------------------------------------------------------------Aggregate  (cost=18262.35..18262.36 rows=1 width=8)->  Nested Loop  (cost=1.23..15762.35 rows=1000000 width=0)->  Seq Scan on flating_t3  (cost=0.00..1443.00 rows=100000 width=0)->  Materialize  (cost=1.23..1819.37 rows=10 width=0)->  Hash Right Join  (cost=1.23..1819.32 rows=10 width=0)Hash Cond: (flating_t2.b1 = flating_t1.a1)->  Seq Scan on flating_t2  (cost=0.00..1443.00 rows=100000 width=4)->  Hash  (cost=1.10..1.10 rows=10 width=4)->  Seq Scan on flating_t1  (cost=0.00..1.10 rows=10 width=4)
(9 rows)czg=# with a as (select * from FLATING_T1 LEFT JOIN FLATING_T2 ON A1=B1)
SELECT COUNT(*) FROM a,FLATING_T3;count
---------1000000
(1 row)

九、通过Hint改变执行计划

1、原执行计划

czg=# set enable_hashjoin = on;
SETczg=# explain (analyze,verbose,buffers) select * from tenk1 t1, tenk2 t2 where t1.id = t2.id and t1.name = '一';QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------Hash Join  (cost=374.29..774.70 rows=1 width=32) (actual time=4.817..11.239 rows=2 loops=1)Output: t1.id, t1.num, t1.name, t1.sex, t2.id, t2.num, t2.name, t2.sexHash Cond: (t2.id = t1.id)Buffers: shared hit=226->  Seq Scan on public.tenk2 t2  (cost=0.00..322.02 rows=20902 width=16) (actual time=0.012..2.577 rows=20902 loops=1)Output: t2.id, t2.num, t2.name, t2.sexBuffers: shared hit=113->  Hash  (cost=374.28..374.28 rows=1 width=16) (actual time=4.794..4.795 rows=2 loops=1)Output: t1.id, t1.num, t1.name, t1.sexBuckets: 1024  Batches: 1  Memory Usage: 9kBBuffers: shared hit=113->  Seq Scan on public.tenk1 t1  (cost=0.00..374.28 rows=1 width=16) (actual time=0.010..4.787 rows=2 loops=1)Output: t1.id, t1.num, t1.name, t1.sexFilter: ((t1.name)::text = '一'::text)Rows Removed by Filter: 20901Buffers: shared hit=113Planning Time: 0.199 msExecution Time: 11.274 ms
(18 rows)

2、Hint提示后的执行计划

czg=# explain (analyze,verbose,buffers) select /*+NestLoop(t2 t1)*/* from tenk1 t1, tenk2 t2 where t1.id = t2.id and t1.name = '一';QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------Nested Loop  (cost=0.00..957.57 rows=1 width=32) (actual time=0.025..15.314 rows=2 loops=1)Output: t1.id, t1.num, t1.name, t1.sex, t2.id, t2.num, t2.name, t2.sexJoin Filter: (t1.id = t2.id)Rows Removed by Join Filter: 41802Buffers: shared hit=339->  Seq Scan on public.tenk1 t1  (cost=0.00..374.28 rows=1 width=16) (actual time=0.015..3.748 rows=2 loops=1)Output: t1.id, t1.num, t1.name, t1.sexFilter: ((t1.name)::text = '一'::text)Rows Removed by Filter: 20901Buffers: shared hit=113->  Seq Scan on public.tenk2 t2  (cost=0.00..322.02 rows=20902 width=16) (actual time=0.005..2.469 rows=20902 loops=2)Output: t2.id, t2.num, t2.name, t2.sexBuffers: shared hit=226Planning Time: 0.153 msExecution Time: 15.344 ms
(15 rows)

3、固定执行计划

由于生产环境中有些应用是不可以修改代码的,及时是加Hint也不可以,pg_hint_plan支持固定执行计划,下面我们来看一下:

(1)hint_plan.hints表介绍

czg=# \d hint_plan.hintsTable "hint_plan.hints"Column       |  Type   | Collation | Nullable |                   Default
-------------------+---------+-----------+----------+---------------------------------------------id                | integer |           | not null | nextval('hint_plan.hints_id_seq'::regclass)norm_query_string | text    |           | not null | application_name  | text    |           | not null | hints             | text    |           | not null |
Indexes:"hints_pkey" PRIMARY KEY, btree (id)"hints_norm_and_app" UNIQUE, btree (norm_query_string, application_name)
列名 含义
id 唯一标识,自动增长的,我们不用维护。
norm_query_string 需要加Hint的Sql,查询中的常量必须替换为?。
application_name 应用的名字,为空字符串的话,表示任何的会话都可以。
hints 需要加的Hint。

(2)插入数据

czg=# insert into hint_plan.hints(norm_query_string,application_name,hints)
czg-# values ('explain (analyze,verbose,buffers) select from tenk1 t1, tenk2 t2 where t1.id = t2.id and t1.name = ?;','','NestLoop(t2 t1)');INSERT 0 1czg=#  select * from hint_plan.hints;id |                                           norm_query_string                                           | application_name |      hints ----+-------------------------------------------------------------------------------------------------------+------------------+------------
-----14 | explain (analyze,verbose,buffers) select from tenk1 t1, tenk2 t2 where t1.id = t2.id and t1.name = ?; |                  | NestLoop(t2t1)
(1 row)

(3)开启参数pg_hint_plan.enable_hint_table

默认参数pg_hint_plan.enable_hint_table是关闭的

czg=# show pg_hint_plan.enable_hint_table;pg_hint_plan.enable_hint_table
--------------------------------off
(1 row)

当前会话生效:

czg=# set pg_hint_plan.enable_hint_table=on;
SET

永久生效,修改完此参数记得重启数据库:

czg=# alter system  set pg_hint_plan.enable_hint_table=on;
ALTER SYSTEM

(4)pg_hint_plan参数介绍

参数名 描述
pg_hint_plan.enable_hint True 为启动 .pg_hint_plan
pg_hint_plan.enable_hint_table True 为可以在表上加Hint
pg_hint_plan.parse_messages 指定提示解析错误的日志级别。有效值为.error ,warning ,notice ,info ,log ,debug
pg_hint_plan.debug_print 控制调试打印和详细信息。有效的值是off ,on ,detailed ,verbose
pg_hint_plan.message_level 指定调试打印的消息级别。有效值为error ,warning ,notice ,info ,log ,debug

默认为:

czg=# show pg_hint_plan.enable_hint;pg_hint_plan.enable_hint
--------------------------on
(1 row)czg=# show pg_hint_plan.enable_hint_table;pg_hint_plan.enable_hint_table
--------------------------------off
(1 row)czg=# show pg_hint_plan.parse_messages;pg_hint_plan.parse_messages
-----------------------------info
(1 row)czg=# show pg_hint_plan.debug_print;pg_hint_plan.debug_print
--------------------------off
(1 row)czg=# show pg_hint_plan.message_level ;pg_hint_plan.message_level
----------------------------log
(1 row)

(5)查看计划是否固定

执行计划固定成功

czg=# explain (analyze,verbose,buffers) select from tenk1 t1, tenk2 t2 where t1.id = t2.id and t1.name = '一';QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------Nested Loop  (cost=4.33..1910.93 rows=5 width=0) (actual time=0.051..33.639 rows=5 loops=1)Join Filter: (t1.id = t2.id)Rows Removed by Join Filter: 104505Buffers: shared hit=118->  Seq Scan on public.tenk2 t2  (cost=0.00..322.02 rows=20902 width=4) (actual time=0.008..2.858 rows=20902 loops=1)Output: t2.id, t2.num, t2.name, t2.sexBuffers: shared hit=113->  Materialize  (cost=4.33..21.27 rows=5 width=4) (actual time=0.000..0.000 rows=5 loops=20902)Output: t1.idBuffers: shared hit=5->  Bitmap Heap Scan on public.tenk1 t1  (cost=4.33..21.25 rows=5 width=4) (actual time=0.038..0.045 rows=5 loops=1)Output: t1.idRecheck Cond: ((t1.name)::text = '一'::text)Heap Blocks: exact=3Buffers: shared hit=5->  Bitmap Index Scan on tenk1_index  (cost=0.00..4.32 rows=5 width=0) (actual time=0.032..0.032 rows=5 loops=1)Index Cond: ((t1.name)::text = '一'::text)Buffers: shared hit=2Query Identifier: 6941249274061524011Planning Time: 0.159 msExecution Time: 33.671 ms
(21 rows)

(6)注意点

hint_plan.hints表中的Sql必须和实际执行的Sql一模一样,如果多一个空格,计划就变了,如下:

czg=# explain (analyze,verbose,buffers) select from tenk1 t1,  tenk2 t2 where t1.id = t2.id and t1.name = '一';QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------Hash Join  (cost=21.31..526.27 rows=5 width=0) (actual time=0.062..7.982 rows=5 loops=1)Hash Cond: (t2.id = t1.id)Buffers: shared hit=118->  Seq Scan on public.tenk2 t2  (cost=0.00..322.02 rows=20902 width=4) (actual time=0.008..3.052 rows=20902 loops=1)Output: t2.id, t2.num, t2.name, t2.sexBuffers: shared hit=113->  Hash  (cost=21.25..21.25 rows=5 width=4) (actual time=0.046..0.048 rows=5 loops=1)Output: t1.idBuckets: 1024  Batches: 1  Memory Usage: 9kBBuffers: shared hit=5->  Bitmap Heap Scan on public.tenk1 t1  (cost=4.33..21.25 rows=5 width=4) (actual time=0.029..0.037 rows=5 loops=1)Output: t1.idRecheck Cond: ((t1.name)::text = '一'::text)Heap Blocks: exact=3Buffers: shared hit=5->  Bitmap Index Scan on tenk1_index  (cost=0.00..4.32 rows=5 width=0) (actual time=0.022..0.022 rows=5 loops=1)Index Cond: ((t1.name)::text = '一'::text)Buffers: shared hit=2Query Identifier: 6941249274061524011Planning Time: 0.144 msExecution Time: 8.017 ms
(21 rows)

还有一点hint_plan.hints表如果只存select后面的语句,不存explain,那我们在Psql中执行explain select语句,也是不能固定计划的,实际生产中使用这个固定计划的功能,就不能通过explain来确定计划是否固定,需要实际执行一下,通过快慢来判断是否生效。

Postgresql学习04-pg_hint_plan安装及使用、Sql优化小知识相关推荐

  1. 学习笔记之-MySql高级之sql优化

    一 Mysql简介 概述 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司. M/SQL是一种关联数据库管理系统,将数据保存在不同的表中,而不是将所有数据 ...

  2. VC学习资料收集(12):VC小知识总结

    VC小知识总结! (1) 如何通过代码获得应用程序主窗口的 指针? 主窗口的 指针保存在CWinThread::m_pMainWnd中,调用AfxGetMainWnd实现. AfxGetMainWnd ...

  3. ROS-语音控制-会说话的机器人(1)-ubuntu16.04上安装运行snowboy

    ROS-语音控制-会说话的机器人(1)-ubuntu16.04上安装运行snowboy 参考ROS小课堂教程,再ubuntu16.04上安装运行snowboy,并解决问题 ROS小课堂安装snowbo ...

  4. 中秋节,送上一次非常有趣的SQL优化实战经历

    点击上方"搜云库技术团队",选择"设为星标" 回复"1024"或"面试题"获取4T学习资料 补充:看到好多朋友后台留言说 ...

  5. DB2分页SQL优化(宝,我优化了分页,每分钟都想你的夜)

    前言: 最近,项目中的一个 DB2分页查询很慢 ,组长将此分页的优化分派给了我:然后一顿优化(乱操作)后,将DB2分页查询耗时降到了比较满意的情况,[ 开森 ]: 然后马上将结果报告了组长,组长查看我 ...

  6. 一次DB2分页语句的优化,带你拨开分页SQL优化的外衣

    一次DB2分页语句的优化,带你拨开分页SQL优化的外衣 1 前言: 最近,项目中的一个 DB2分页查询很慢 ,组长将此分页的优化分派给了我:然后一顿优化(乱操作)后,将DB2分页查询耗时降到了比较满意 ...

  7. PostgreSQL学习总结(1)—— PostgreSQL 入门简介与安装

    PostgreSQL 是一个免费的对象-关系数据库服务器(ORDBMS),在灵活的BSD许可证下发行.PostgreSQL 开发者把它念作 post-gress-Q-L.PostgreSQL 的 Sl ...

  8. Ubuntu Server 20.04 LTS 安装配置 PostgreSQL

    1 & 环境准备 上面一篇文章我们讲解了在 Windows 环境上面安装 PostgreSQL 的简单介绍,准备环境基本一样. ubuntu server 20.04 镜像下载地址 => ...

  9. Caffe学习笔记2--Ubuntu 14.04 64bit 安装Caffe(GPU版本)

    0.检查配置 1. VMWare上运行的Ubuntu,并不能支持真实的GPU(除了特定版本的VMWare和特定的GPU,要求条件严格,所以我在VMWare上搭建好了Caffe环境后,又重新在Windo ...

最新文章

  1. Redis集群管理方式
  2. MySQL 为什么表的数据删除一般,表文件大小不变?
  3. mysql event scheduler机制 与 动态表名创建
  4. 【爬坑】Vim 文档加密 解密
  5. Safari浏览器不支持let声明的解决方式
  6. 北美KubeCon新风,正把K8S魔力带向边缘计算
  7. MySQL子查询作为列_mysql 列子查询
  8. Enterprise Library启用签名后发生 PublicKeyToken错误,HRESULT:0x80131040解决
  9. screen,client,page三种确定鼠标坐标的区别和原生JS事件写法,区别于Jquery的$.on(x,y);和$.click()...
  10. Lesson 3.1 - Python Core Data Types
  11. 水晶报表2008部署
  12. 平板电脑刷机加供电系统改造
  13. 使用Javascript动态添加和删除元素
  14. Qtum量子链QIP-5提案:在智能合约交易输出脚本上增加签名证明,允许用户以代付方式调用合约
  15. 显示器经典故障以及处理办法
  16. 活体检测Face Anti-spoofing前世今生:作者(Fisher Yu )
  17. 视频教程-C++QT5跨平台界面编程原理和实战大全-C/C++
  18. 使用了 23 的 Java 真的收费了吗?
  19. 订单23系统服务器,死亡搁浅寻物系统服务器任务内容介绍-死亡搁浅寻物系统服务器任务流程详情_牛游戏网...
  20. HTML3/CSS基础

热门文章

  1. 惊呆了,竟然可以用这种方式秒建Redis集群?
  2. MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk.问题解决
  3. 【分布族谱】正态分布和卡方分布的关系
  4. 使用node搭建后台管理系统(1)
  5. ssh -o 常用选项
  6. 一个n维井字棋的游戏
  7. PostgreSql优势是什么?
  8. SQL Server 2008 正式版下载地址+安装指南+序列号
  9. 基于FPGA的UART异步串行通信发送模块设计与实现
  10. 学习大数据的第29天——Linux指令的学习以及一些面试题