跳转至

`HINT`查询

hint查询

Hint为用户提供了直接影响执行计划生成的手段,用户可以通过指定join顺序、join/scan方法、指定结果行数等多个手段来进行执行计划的调优,以提升查询的性能。

功能描述

Hint查询通过在SELECT关键字后输入如下形式指定:

/*+ <plan hint>*/

可以同时指定多个hint,之间使用空格分隔。hint只能影响当前层的计划,对于子查询计划的hint,需要在子查询的select关键字后指定hint。

例如:

select /*+ <plan_hint1> <plan_hint2> */ * from t1, (select /*+ <plan_hint3> */ * from t2) where 1=1;

其中<plan_hint1>,<plan_hint2>为外层查询的hint,<plan_hint3>为内层子查询的hint。

须知:

如果在视图定义(CREATE VIEW)时指定hint,则在该视图每次被应用时会使用该hint。

支持类型

当前版本Hint支持的类型如下,后续版本会进行增强。

  • 指定Join顺序的Hint - leading hint。
  • 指定Join方式的Hint,仅支持除semi/anti join,unique plan之外的常用hint。
  • 指定结果集行数的Hint。
  • 指定Scan方式的Hint,仅支持常用的tablescan,indexscan和indexonlyscan的hint。
  • 指GUC参数设置的Hint。

注意事项

Set的hint仅对影响计划生成的GUC参数有效。

用例

后续章节用于说明hint功能所用到的表的创建命令如下:

create table src(a int);
insert into src values(1);

create table hint_t1(a int, b int, c int);
insert into hint_t1 select generate_series(1, 2000), generate_series(1, 1000), generate_series(1, 500) from src;

create table hint_t2(a int, b int, c int);
insert into hint_t2 select generate_series(1, 1000), generate_series(1, 500), generate_series(1, 100) from src;

create table hint_t3(a int, b int, c int);
insert into hint_t3 select generate_series(1, 100), generate_series(1, 50), generate_series(1, 25) from src;

create table hint_t4(a int, b int, c int);
insert into hint_t4 select generate_series(1, 10), generate_series(1, 5), generate_series(1, 2) from src;

create table hint_t5(a int, b int, c int);
insert into hint_t5 select generate_series(1, 5), generate_series(1, 5), generate_series(1, 2) from src;

analyze hint_t1;
analyze hint_t2;
analyze hint_t3;
analyze hint_t4;
analyze hint_t5;

scan hint

指明scan使用的方法,可以是tablescan、indexscan和indexonlyscan。

语法格式

[no] tablescan|indexscan|indexonlyscan(table [index])

参数说明

  • no表示hint的scan方式不使用。

  • table表示hint指定的表,只能指定一个表,如果表存在别名应优先使用别名进行hint。

  • index表示使用indexscan或indexonlyscan的hint时,指定的索引名称,当前只能指定一个。

说明:

对于indexscan或indexonlyscan,只有hint的索引属于hint的表时,才能使用该hint。

示例

为了hint使用索引扫描,需要首先在表hint_t1的a列上创建索引,名称为index_t1_a。

create index index_t1_a on hint_t1(a);

不使用hint的查询计划:

explain
select * from hint_t1 where a > 1;

计划:

 Gather Motion 3:1  (slice1; executors: 3)  (cost=0.00..35.98 rows=1998 width=12)
   ->  Seq Scan on hint_t1  (cost=0.00..9.34 rows=666 width=12)
         Filter: (a > 1)
 Optimizer: SeaboxSQL query optimizer
(4 rows)

使用hint的查询计划:

explain
select /*+ indexscan(hint_t1 index_t1_a)*/* from hint_t1 where a > 1;

该hint表示:hint_t1表使用索引index_t1_a进行扫描。生成计划如下所示:

 Gather Motion 3:1  (slice1; executors: 3)  (cost=0.15..46.46 rows=1998 width=12)
   ->  Index Scan using index_t1_a on hint_t1  (cost=0.15..19.82 rows=666 width=12)
         Index Cond: (a > 1)
 Optimizer: SeaboxSQL query optimizer
(4 rows)

join hint

功能描述

指明Join使用的方法,可以为Nested Loop,Hash Join和Merge Join。

语法格式

[no] nestloop|hashjoin|mergejoin(table_list)

参数说明

  • no表示hint的join方式不使用。

  • table_list为表示hint表集合的字符串,该字符串中的表与join_table_list相同,只是中间不允许出现括号指定join的优先级。

例如:

no nestloop(t1 t2 t3)表示:生成t1,t2,t3三表连接计划时,不使用nestloop。三表连接计划可能是t2 t3先join,再跟t1 join,或t1 t2先join,再跟t3 join。此hint只影响最后一次join的join方式,对于两表连接的方法不限制。如果需要,可以单独指定,例如:任意表均不允许nestloop连接,且希望t2 t3先join,则增加hint:no nestloop(t2 t3)。

示例

不使用hint的join查询:

explain (costs off)
select
  *
from hint_t1 as t1
join hint_t2 as t2
on(t1.a = t2.b)
join hint_t3 as t3
on (t2.a = t3.b)
join hint_t4 as t4
on (t4.c = t3.c);

结果:

 Gather Motion 3:1  (slice1; executors: 3)
   ->  Nested Loop
         ->  Redistribute Motion 3:3  (slice2; executors: 3)
               Hash Key: t2.b
               ->  Hash Join
                     Hash Cond: (t2.a = t3.b)
                     ->  Seq Scan on hint_t2 t2
                     ->  Hash
                           ->  Redistribute Motion 3:3  (slice3; executors: 3)
                                 Hash Key: t3.b
                                 ->  Hash Join
                                       Hash Cond: (t3.c = t4.c)
                                       ->  Seq Scan on hint_t3 t3
                                       ->  Hash
                                             ->  Broadcast Motion 3:3  (slice4; executors: 3)
                                                   ->  Seq Scan on hint_t4 t4
         ->  Index Scan using index_t1_a on hint_t1 t1
               Index Cond: (a = t2.b)
 Optimizer: SeaboxSQL query optimizer
(19 rows)

使用hint的join查询:

explain (costs off)
select /*+ hashjoin(t1 t2) mergejoin (t1 t2 t3) nestloop(t1 t2 t3 t4)*/
  *
from hint_t1 as t1
join hint_t2 as t2
on(t1.a = t2.b)
join hint_t3 as t3
on (t2.a = t3.b)
join hint_t4 as t4
on (t4.c = t3.c);

该hint表示:t1,t2使用hash join,结果和t3作merge join(顺序不限定);上面结果和t4作nestloop(顺序不限定)。生成计划如下所示:

 Gather Motion 3:1  (slice1; executors: 3)
   ->  Nested Loop
         Join Filter: (t3.c = t4.c)
         ->  Merge Join
               Merge Cond: (t2.a = t3.b)
               ->  Sort
                     Sort Key: t2.a
                     ->  Hash Join
                           Hash Cond: (t1.a = t2.b)
                           ->  Seq Scan on hint_t1 t1
                           ->  Hash
                                 ->  Redistribute Motion 3:3  (slice2; executors: 3)
                                       Hash Key: t2.b
                                       ->  Seq Scan on hint_t2 t2
               ->  Sort
                     Sort Key: t3.b
                     ->  Broadcast Motion 3:3  (slice3; executors: 3)
                           ->  Seq Scan on hint_t3 t3
         ->  Materialize
               ->  Broadcast Motion 3:3  (slice4; executors: 3)
                     ->  Seq Scan on hint_t4 t4
 Optimizer: SeaboxSQL query optimizer
(22 rows)

leading hint

功能描述

指明join的顺序,包括内外表顺序。

语法格式

  • 仅指定join顺序,不指定内外表顺序。
leading(join_table_list) 
  • 同时指定join顺序和内外表顺序,内外表顺序仅在最外层生效。
leading((join_table_list)) 

参数说明

join_table_list为表示表join顺序的hint字符串,可以包含当前层的任意个表(别名),或对于子查询提升的场景,也可以包含子查询的hint别名,同时任意表可以使用括号指定优先级,表之间使用空格分隔。

须知:

表只能用单个字符串表示,不能带schema。 表如果存在别名,需要优先使用别名来表示该表。

join table list中指定的表需要满足以下要求,否则会报语义错误。

  • list中的表必须在当前层或提升的子查询中存在。
  • list中的表在当前层或提升的子查询中必须是唯一的。如果不唯一,需要使用不同的别名进行区分。
  • 同一个表只能在list里出现一次。
  • 如果表存在别名,则list中的表需要使用别名。

例如:

leading(t1 t2 t3 t4 t5)表示:t1, t2, t3, t4, t5先join,五表join顺序及内外表不限。

leading((t1 t2 t3 t4 t5))表示:t1和t2先join,t2做内表;再和t3 join,t3做内表;再和t4 join,t4做内表;再和t5 join,t5做内表。

leading(t1 (t2 t3 t4) t5)表示:t2, t3, t4先join,内外表不限;再和t1, t5 join,内外表不限。

leading((t1 (t2 t3 t4) t5))表示:t2, t3, t4先join,内外表不限;在最外层,t1再和t2, t3, t4的join表join,t1为外表,再和t5 join,t5为内表。

leading((t1 (t2 t3) t4 t5)) leading((t3 t2))表示:t2, t3先join,t2做内表;然后再和t1 join,t2, t3的join表做内表;然后再依次跟t4, t5做join,t4, t5做内表。

示例

不使用hint的茶查询:

explain (costs off)
select
  *
from hint_t1 as t1
join hint_t2 as t2
on(t1.a = t2.b)
join hint_t3 as t3
on (t2.a = t3.b)
join hint_t4 as t4
on (t4.c = t3.c);

结果:

 Gather Motion 3:1  (slice1; executors: 3)
   ->  Nested Loop
         ->  Redistribute Motion 3:3  (slice2; executors: 3)
               Hash Key: t2.b
               ->  Hash Join
                     Hash Cond: (t2.a = t3.b)
                     ->  Seq Scan on hint_t2 t2
                     ->  Hash
                           ->  Redistribute Motion 3:3  (slice3; executors: 3)
                                 Hash Key: t3.b
                                 ->  Hash Join
                                       Hash Cond: (t3.c = t4.c)
                                       ->  Seq Scan on hint_t3 t3
                                       ->  Hash
                                             ->  Broadcast Motion 3:3  (slice4; executors: 3)
                                                   ->  Seq Scan on hint_t4 t4
         ->  Index Scan using index_t1_a on hint_t1 t1
               Index Cond: (a = t2.b)
 Optimizer: SeaboxSQL query optimizer
(19 rows)

使用leading hint的查询:

explain (costs off)
select /*+ leading(((t1 t2) (t3 t4))) */
  *
from hint_t1 as t1
join hint_t2 as t2
on(t1.a = t2.b)
join hint_t3 as t3
on (t2.a = t3.b)
join hint_t4 as t4
on (t4.c = t3.c);

结果:

 Gather Motion 3:1  (slice1; executors: 3)
   ->  Hash Join
         Hash Cond: (t2.a = t3.b)
         ->  Hash Join
               Hash Cond: (t1.a = t2.b)
               ->  Seq Scan on hint_t1 t1
                     BloomFilter consumer: bf_index: 2, ((tle_resno: 1, var: t1.a))
               ->  Hash
                     ->  Redistribute Motion 3:3  (slice2; executors: 3)
                           Hash Key: t2.b
                           BloomFilter producer: bf_index: 2, need_redist: 0, log2(ndistinct): 8, ((tle_resno: 2, var: t2.b))
                           ->  Seq Scan on hint_t2 t2
                                 BloomFilter consumer: bf_index: 1, ((tle_resno: 1, var: t2.a))
         ->  Hash
               ->  Broadcast Motion 3:3  (slice3; executors: 3)
                     BloomFilter producer: bf_index: 1, need_redist: 1, log2(ndistinct): 5, ((tle_resno: 2, var: t3.b))
                     ->  Hash Join
                           Hash Cond: (t3.c = t4.c)
                           ->  Seq Scan on hint_t3 t3
                                 BloomFilter consumer: bf_index: 3, ((tle_resno: 3, var: t3.c))
                           ->  Hash
                                 ->  Broadcast Motion 3:3  (slice4; executors: 3)
                                       BloomFilter producer: bf_index: 3, need_redist: 0, log2(ndistinct): 1, ((tle_resno: 3, var: t4.c))
                                       ->  Seq Scan on hint_t4 t4
 Optimizer: SeaboxSQL query optimizer
(25 rows)

该hint表示:表之间的join关系是:t1和t2先join, 结果作为外表;t3和t4作join, 结果作为内表。

row hint

功能描述

指明中间结果集的大小,支持绝对值和相对值的hint。

语法格式

rows(table_list #|+|-|* const)

参数说明

  • #,+,-,*,进行行数估算hint的四种操作符号。#表示直接使用后面的行数进行hint。+,-,*表示对原来估算的行数进行加、减、乘操作,运算后的行数最小值为1行。table_list为hint对应的单表或多表join结果集,与Join方式的Hinttable_list相同。

  • const可以是任意非负数,支持科学计数法。

例如:

rows(t1 #5)表示:指定t1表的结果集为5行。

rows(t1 t2 t3 *1000)表示:指定t1, t2, t3 join完的结果集的行数乘以1000。

建议

  • 推荐使用两个表*的hint。对于两个表的采用*操作符的hint,只要两个表出现在join的两端,都会触发hint。例如:设置hint为rows(t1 t2 * 3),对于(t1 t3 t4)和(t2 t5 t6)join时,由于t1和t2出现在join的两端,所以其join的结果集也会应用该hint规则乘以3。
  • rows hint支持在单表、多表、function table及subquery scan table的结果集上指定hint。

示例

不使用rows hint的查询:

explain
select
  *
from hint_t1 as t1
join hint_t2 as t2
on(t1.a = t2.b)
join hint_t3 as t3
on (t2.a = t3.b);

结果为:

 Gather Motion 3:1  (slice1; executors: 3)  (cost=2.56..11.90 rows=25 width=36)
   ->  Nested Loop  (cost=2.56..11.56 rows=8 width=36)
         ->  Redistribute Motion 3:3  (slice2; executors: 3)  (cost=2.40..8.37 rows=17 width=24)
               Hash Key: t2.b
               ->  Hash Join  (cost=2.40..8.03 rows=17 width=24)
                     Hash Cond: (t2.a = t3.b)
                     ->  Seq Scan on hint_t2 t2  (cost=0.00..4.33 rows=333 width=12)
                     ->  Hash  (cost=1.99..1.99 rows=33 width=12)
                           ->  Redistribute Motion 3:3  (slice3; executors: 3)  (cost=0.00..1.99 rows=33 width=12)
                                 Hash Key: t3.b
                                 ->  Seq Scan on hint_t3 t3  (cost=0.00..1.33 rows=33 width=12)
         ->  Index Scan using index_t1_a on hint_t1 t1  (cost=0.15..0.18 rows=1 width=12)
               Index Cond: (a = t2.b)
 Optimizer: SeaboxSQL query optimizer
(14 rows)
explain
select  /*+ rows(t2 t3 #230)*/
  *
from hint_t1 as t1
join hint_t2 as t2
on(t1.a = t2.b)
join hint_t3 as t3
on (t2.a = t3.b);

该hint表示:t2,t3关联的结果集估算行数制定为230。生成计划如下所示:

 Gather Motion 3:1  (slice1; executors: 3)  (cost=12.13..17.91 rows=25 width=36)
   ->  Merge Join  (cost=12.13..17.57 rows=8 width=36)
         Merge Cond: (t1.a = t2.b)
         ->  Index Scan using index_t1_a on hint_t1 t1  (cost=0.15..18.16 rows=667 width=12)
         ->  Sort  (cost=11.98..12.17 rows=77 width=24)
               Sort Key: t2.b
               ->  Redistribute Motion 3:3  (slice2; executors: 3)  (cost=2.40..9.57 rows=77 width=24)
                     Hash Key: t2.b
                     ->  Hash Join  (cost=2.40..8.03 rows=77 width=24)
                           Hash Cond: (t2.a = t3.b)
                           ->  Seq Scan on hint_t2 t2  (cost=0.00..4.33 rows=333 width=12)
                           ->  Hash  (cost=1.99..1.99 rows=33 width=12)
                                 ->  Redistribute Motion 3:3  (slice3; executors: 3)  (cost=0.00..1.99 rows=33 width=12)
                                       Hash Key: t3.b
                                       ->  Seq Scan on hint_t3 t3  (cost=0.00..1.33 rows=33 width=12)
 Optimizer: SeaboxSQL query optimizer
(16 rows)

其中t2,t3的结果集(Hash Join (cost=2.40..8.03 rows=77 width=24))的真实数值77 = 230 / executor_nums。

set hint

功能描述

设置本次查询执行内生效的查询优化相关的GUC参数。支持子查询(不可提升)单独指定set hint。

语法格式

set(param value)

参数说明

  • param表示参数名
  • value表示参数的取值

说明: - 设置不是在计划生成阶段生效的参数或者参数设置非法,set hint不生效。

示例

不使用set hint的查询:

explain (costs off)
select 1 from hint_t1 as t1 join
(select sum(t2.a) as a from hint_t2 as t2 join hint_t3 as t3
on (t2.a = t3.a)) as AA on(t1.a = AA.a);

结果:

 Gather Motion 3:1  (slice1; executors: 3)
   ->  Hash Join
         Hash Cond: (t1.a = (sum(t2.a)))
         ->  Seq Scan on hint_t1 t1
               BloomFilter consumer: bf_index: 1, ((tle_resno: 1, var: t1.a))
         ->  Hash
               ->  Redistribute Motion 1:3  (slice2; executors: 1)
                     Hash Key: (sum(t2.a))
                     BloomFilter producer: bf_index: 1, need_redist: 0, log2(ndistinct): 0, ((tle_resno: 1, var: (sum(t2.a))))
                     ->  Finalize Aggregate
                           ->  Gather Motion 3:1  (slice3; executors: 3)
                                 ->  Partial Aggregate
                                       ->  Hash Join
                                             Hash Cond: (t2.a = t3.a)
                                             ->  Seq Scan on hint_t2 t2
                                                   BloomFilter consumer: bf_index: 2, ((tle_resno: 1, var: t2.a))
                                             ->  Hash
                                                   ->  Seq Scan on hint_t3 t3
                                                         BloomFilter producer: bf_index: 2, need_redist: 0, log2(ndistinct): 6, ((tle_resno: 1, var: t3.a))
 Optimizer: SeaboxSQL query optimizer
(20 rows)

上面计划显示: 子查询不能提升,t2,t3作hash join; 最后跟t1作hash join。

使用set hint的查询:

explain (costs off)
select /*+ set(enable_hashjoin off)*/  1 from hint_t1 as t1 join
(select /*+  set(enable_hashjoin off)*/  sum(t2.a) as a from hint_t2 as t2 join hint_t3 as t3
on (t2.a = t3.a)) as AA on(t1.a = AA.a);

结果:

 Gather Motion 3:1  (slice1; executors: 3)
   ->  Merge Join
         Merge Cond: (t1.a = (sum(t2.a)))
         ->  Index Only Scan using index_t1_a on hint_t1 t1
         ->  Sort
               Sort Key: (sum(t2.a))
               ->  Redistribute Motion 1:3  (slice2; executors: 1)
                     Hash Key: (sum(t2.a))
                     ->  Finalize Aggregate
                           ->  Gather Motion 3:1  (slice3; executors: 3)
                                 ->  Partial Aggregate
                                       ->  Merge Join
                                             Merge Cond: (t2.a = t3.a)
                                             ->  Sort
                                                   Sort Key: t2.a
                                                   ->  Seq Scan on hint_t2 t2
                                             ->  Sort
                                                   Sort Key: t3.a
                                                   ->  Seq Scan on hint_t3 t3
 Optimizer: SeaboxSQL query optimizer
(20 rows)

上面查询计划显示:外层查询和子查询都没有使用hash join。