自适应两阶段Hash Distinct速记

本文所述技术,适用于 Hash Distinct 下压处理,Hash GroupBy 下压处理

基础技术

create table tbl1 (c1 int, c2 int) partition by hash(c2) partitions 3;
select /*+ parallel(3) use_hash_distinct */ distinct c1 from tbl1

下面讲解分布式场景下如何实现高效的 distinct 计算。

最简单的计划是各个机器上做初步的去重,然后将数据收集到中心节点做最终的去重

OceanBase(admin@test)>explain select /*+ parallel(3) use_hash_distinct */ distinct c1 from tbl1\G
Query Plan: 
==========================================================
|ID|OPERATOR                     |NAME    |EST. ROWS|COST|
----------------------------------------------------------
|0 |HASH DISTINCT                |        |1        |46  |
|1 | PX COORDINATOR              |        |1        |46  |
|2 |  EXCHANGE OUT DISTR         |:EX10001|1        |46  |
|3 |   HASH DISTINCT             |        |1        |46  |
|4 |    PX BLOCK ITERATOR        |        |1        |46  |
|5 |     TABLE SCAN              |tbl1    |1        |46  |
==========================================================

考虑到中心节点可能会收到大量初步去重后的数据,导致中心节点压力过大,引入两阶段 Hash Distinct 算法。

首先,各个机器上做独立的去重,然后对去重后的结果做 HASH 重分区,分发到多台机器上做第二阶段的去重。各个机器得到的去重结果就是最终结果,汇聚到中心节点直接输出即可。

OceanBase(admin@test)>explain select /*+ parallel(3) use_hash_distinct */ distinct c1 from tbl1\G
Query Plan: 
==========================================================
|ID|OPERATOR                     |NAME    |EST. ROWS|COST|
----------------------------------------------------------
|0 |PX COORDINATOR               |        |1        |46  |
|1 | EXCHANGE OUT DISTR          |:EX10001|1        |46  |
|2 |  HASH DISTINCT              |        |1        |46  |
|3 |   EXCHANGE IN DISTR         |        |1        |46  |
|4 |    EXCHANGE OUT DISTR (HASH)|:EX10000|1        |46  |
|5 |     HASH DISTINCT           |        |1        |46  |
|6 |      PX BLOCK ITERATOR      |        |1        |46  |
|7 |       TABLE SCAN            |tbl1    |1        |46  |
==========================================================

自适应技术

上面的两种方式足以应对大部分场景。为了进一步优化一种场景场景,引入自适应技术。

考虑场景:优化器已经生成了两阶段 Hash Distinct 算法,但各个节点上的 c1 分布非常离散,使得第一阶段 Hash Distinct 基本都是在做无用功。如果 c1 的值非常离散,理想情况下 5 号 Hash Distinct 算子应该什么都不做,直接 bypass 数据,让它去 2 号 Hash Distinct 算子里计算最终结果。

更复杂的场景:优化器已经生成了两阶段 Hash Distinct 算法,但各个节点上的 c1 的分区再数据流上呈现出时而离散,时而紧凑的特征。这时 5 号算子应该根据数据流的特征,有时做 distinct 计算,有时直接 bypass 数据。

对于上面的场景,都可以用自适应的技术来解决。

  1. 针对数据流中的数据建立 hash 表,并统计建立 hash 表过程中的命中率
  2. 当 hash 表的大小达到 L1 个字节时判定:
    • 如果历史命中率大于 95%,那么继续扩大 hash 表
    • 如果历史命中率小于 95%,那么废弃 hash 表
  3. 当 hash 表的大小达到 L2 个字节时判定:
    • 如果历史命中率大于 95%,那么继续扩大 hash 表
    • 如果历史命中率小于 95%,那么废弃 hash 表
  4. 当 hash 表的大小达到 L3 个字节时判定:
    • 如果历史命中率大于 95%,那么新来的行要么命中 hash 表,要么直接 bypass 到 2 号Hash Distinct 算子去
    • 如果历史命中率小于 95%,那么废弃 hash 表

讨论

  • L1、L2、L3 的选取,可以基于 CPU 的 L1 缓存大小、L2缓存大小、L3缓存大小来决定。
    95% 这个值只是举例子,在实际的系统里可以通过测试得到。
  • 可以不是 3 层,可以是 2 层,比如 L2、L3 (因为 L1 Cache 实在太小了)
  • 很多时候,决策的最优方向是尽量快速启动 Bypass,而不是提高命中率(反常识吧?)

其它改进

对于高频值,在每次淘汰 Hash Table Cache 时可予以保留。详见参考文献一节。

参考文献

《Adaptive and Big Data Scale Parallel Execution in Oracle》 第2节 GroupBy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值