HBO

Hologres4.0版本开始支持HBO(History-Based Optimization),一种基于Query历史行为分析的查询优化技术,其核心原理是Hologres会记录Query的历史统计信息,通过动态学习Query的历史查询模式、资源消耗和数据分布特征,实时生成最优执行计划,减少资源消耗,提升Query运行性能。

使用限制

  • 4.0及以上版本支持该功能

HBO开启和关闭

4.0版本开始,HBO默认开启,开启后,引擎会默认收集Query的历史统计信息,以便Query后续能使用HBO。如果需要关闭,请执行如下GUC。

--session级别关闭
set hg_adaptive_execution_enable_history_based_optimization = off;  

--DB级别关闭,新建链接生效
alter database <db_name> set hg_adaptive_execution_enable_history_based_optimization = off; 

HBO命中前提条件

命中HBO需要同时满足以下条件:

  1. 执行计划中的任何一张表预估行数大于100000行(rows>100000),或者有任何一张表没有统计信息,预计行数为1000(rows=1000),当满足该条件时Query的统计信息将会被采集进历史统计信息中。可以通过如下GUC修改采集的表预估行数阈值。

    说明

    不建议调整后阈值小于100000行,行数太少会导致HBO频繁采样,带来框架开销影响性能

    --session级别
    set hg_adaptive_execution_enable_hbo_min_estimated_scan_rows =<num>;
    
    --db级别
    alter database <db_name> set hg_adaptive_execution_enable_hbo_min_estimated_scan_rows =<num>;
    
  2. 执行计划中必须有Hash Agg节点

  3. Query必须拥有历史统计信息。会被采集至历史统计信息的条件如下:

    1. 48小时内该Query至少运行过一次

    2. SQL执行的总耗时大于100ms

HBO优化规则

Hologres4.0版本HBO优化规则支持:指定Hash表大小和自适应生成多阶段Agg两种规则。

指定Hash表的大小

Hash Agg的实际执行时,因为并不知道数据量,因此无法确定实际需要的Hash表的大小,Hash表初始化过大,内存消耗增加,初始化太小,会多次Resize Hash表大小来动态调整,HBO可以根据历史执行信息,确定用到的Hash表的大小,根据该值,指导下一次执行。

当历史执行中的Hash表行数大于100000行时(Hash表的大小 != Agg的行数),系统会把历史Hash表行数作为此次执行初始Hash表大小。

可以通过如下GUC修改该规则的命中阈值:

说明

不建议调整后阈值小于100000行,对于小Hash表,性能变化有限,反而HBO框架开销可能影响性能

--session级别修改
set hg_adaptive_execution_hbo_generate_advice_hash_table_size_low_bound =<num>;

--db级别修改,新建链接生效
alter database <db_name> set hg_adaptive_execution_hbo_generate_advice_hash_table_size_low_bound =<num>;

使用示例:

create table t1(a int , b int);
insert into t1 select i,i from generate_series(1,1000000) i;
analyze t1;

--为了观测hbo效果,临时设置hash表的hbo的阈值调到1,生产环境不建议设置为1
set hg_adaptive_execution_hbo_generate_advice_hash_table_size_low_bound = 1;
explain analyze  select a , count(b) from t1 group by a;

多次执行Select后,执行计划如下:

  • Explain Analyze中有[HBO Modification]结果,说明sql已经成功用上HBO。

  • HashAggregate节点中的rows=(48052/47619/47219)为预估的每个worker上的Hash表大小,因为开启了GUC临时将Hash表大小调整为1,使得该Query可以使用HBO。

QUERY PLAN
Gather  (cost=0.00..47.72 rows=1000000 width=12)
[21:1 id=100003 dop=1 time=1/1/1ms rows=1000000(1000000/1000000/1000000) mem=0/0/0B open=0/0/0ms get_next=1/1/1ms * ]
  ->  HashAggregate  (cost=0.00..18.70 rows=1000000 width=12)
        Group Key: a
      [id=4 dop=21 time=11/6/5ms rows=1000000(48052/47619/47219) mem=4/4/4MB open=11/6/5ms get_next=0/0/0ms rehash=1/1/1 hash_mem=3/3/3MB]
        ->  Redistribution  (cost=0.00..6.21 rows=1000000 width=8)
              Hash Key: a
            [21:21 id=100002 dop=21 time=1/0/0ms rows=1000000(48052/47619/47219) mem=0/0/0B open=0/0/0ms get_next=1/0/0ms * ]
              ->  Local Gather  (cost=0.00..5.02 rows=1000000 width=8)
                  [id=2 dop=21 time=0/0/0ms rows=1000000(49152/47619/40960) mem=0/0/0B open=0/0/0ms get_next=0/0/0ms local_dop=1/1/1 * ]
                    ->  Seq Scan on t1  (cost=0.00..5.01 rows=1000000 width=8)
                        [id=1 split_count=13 time=1/0/0ms rows=623168(49152/47936/40960) mem=576/576/576B open=1/0/0ms get_next=0/0/0ms scan_rows=623168(49152/47936/40960)]

ADVICE: 
[node id : 100002] distribution key miss match! table t1 missing distribution keys! ; request distribution columns : a; 

HBO Modification:
[node id: 4] Init Hash Table Size for Agg has been set to:48052.

Query id:[101101885xxxx]
QE version: 2.0
Query Queue: default_queue
======================cost======================
Total cost:[141] ms
History Optimizer cost:[2.024] ms
Optimizer cost:[29] ms
Build execution plan cost:[1] ms
Init execution plan cost:[6] ms
Start query cost:[25] ms
- Queue cost: [0] ms
- Wait schema cost:[0] ms
- Lock query cost:[0] ms
- Create dataset reader cost:[0] ms
- Create split reader cost:[0] ms
Get result cost:[80] ms
- Get the first block cost:[8] ms
====================resource====================
Memory: total 110 MB. Worker stats: max 6 MB, avg 4 MB, min 2 MB, max memory worker id: 2385500434182507053.
CPU time: total 222 ms. Worker stats: max 18 ms, avg 9 ms, min 5 ms, max CPU time worker id: 2388922492294751946.
DAG CPU time stats: max 140 ms, avg 61 ms, min 0 ms, cnt 3, max CPU time dag id: 3.
Fragment CPU time stats: max 140 ms, avg 45 ms, min 0 ms, cnt 4, max CPU time fragment id: 3.
Ec wait time: total 34 ms. Worker stats: max 6 ms, max(max) 2 ms, avg 1 ms, min 0 ms, max ec wait time worker id: 2388922681335810605, max(max) ec wait time worker id: 2388922681335810605.
Read bytes: total 4 MB. Worker stats: max 0 MB, avg 0 MB, min 0 MB, max read bytes worker id: 2388922715412204922.
DAG instance count: total 43. Worker stats: max 3, avg 1, min 1, max DAG instance count worker id: 797329658507415939.
Fragment instance count: total 64. Worker stats: max 4, avg 2, min 1, max fragment instance count worker id: 797329658507415939.

自适应生成多阶段Agg

多阶段Agg可以提前压缩数据,极大的减少数据传输的开销,但是当压缩率较低时,多阶段Agg带来的重复计算,反而影响执行性能。但是在计算之前获得Agg的压缩效果往往比较困难或者误差较大,HBO可以根据历史执行过程中,Agg的数据压缩效果,决定是否生成多阶段Agg。当Agg总行数较多,压缩率高,HBO会强制生成多阶段Agg,对于压缩率低的多阶段Agg,HBO会根据历史执行信息删除多余的Agg,通过动态的使用Partial Agg来提升查询性能。该规则由优化器自适应决定,无需手动调整。

查看历史统计信息的存储大小

HBO依赖历史统计信息,该历史记录会占用一定的实例存储,可以通过如下函数查看占用的存储大小。

--查看HBO历史统计信息占用的存储大小
select PG_SIZE_PRETTY(hologres.hg_hbo_relation_size());

查询结果如下:

pg_size_pretty
----
9940 kB