Apache Doris Join 优化原理详解
发布时间:2023-02-17 13:10:50 所属栏目:Apache 来源:互联网
导读:背景 目标 掌握 Apache Doris Join 优化手段及其实现原理 为代码阅读提供理论基础 Doris 数据划分 不同的 Join 方式非常依赖于对 Doris 中数据划分方式的透彻理解。因此先在这里列举出必要的基础知识。 首先,在 Doris 中数据都以表(Table)的形式进行逻辑
| T1 T2 | 如果能将过滤条件(Runtime Filter)下推到存储引擎,则某些情况可以利用索引(比如 Join 列为 Key 列,可以利用延迟物化能力)来直接减少扫描的数据量,从而大大减少扫描耗时,效果如下: | > HashJoinNode < | | | | | 6000 | 2000 | | | | OlapScanNode OlapScanNode | ^ ^ | | 6000 | 2000 | T1 T2 | 可见,和谓词下推、分区裁剪不同,Runtime Filter 是在运行时动态生成的过滤条件,即在查询运行时解析 Join 条件确定过滤表达式,并将表达式下推给正在读取左表的 ScanNode,从而减少扫描的数据量,进而减少 probe hash table 的次数,避免不必要的 IO 和网络传输。因为其运行时生效的特性,官方认为它是 Adaptive Query Execution 的一种应用。 根据上面的例子,可以推导出场景满足以下的条件时,使用 Runtime Filter 的效果会比较好: 左表大右表小(当右表上还有额外的过滤条件会更理想),因为构建 Runtime Filter 是需要承担计算成本的,包括一些内存的开销,而开销直接取决于右表的实际数据量 左右表 Join 出来的结果很少,说明通过 Runtime Filter 可以过滤掉左表的绝大部分数据 Doris 支持 3 种 Runtime Filter: 一种是 IN,很好理解,将一个 hashset 下推到数据扫描节点。 第二种就是 BloomFilter,就是利用哈希表的数据构造一个 BloomFilter,然后把这个 BloomFilter 下推到查询数据的扫描节点。 最后一种就是 MinMax,就是个 Range 范围,通过右表数据确定 Range 范围之后,下推给数据扫描节点。 工作原理和优劣总结如下: Runtime Filter 类型 工作原理 适用场景 优点 缺点 IN 子查询的方式,实现上是将一个 Hashset 下推到 Scan 节点 Broadcast Join 开销小,过滤效果明显且快速 右表超过一定数据量时会失效,目前 Doris 配置的阈值是 1024 Min/Max 通过右表构建一个 Range 范围,然后将它下推到 Scan 节点 通用 开销小 仅对数值类型有效果;对数值以外类型无法使用 BloomFilter 通过右表构建一个 BloomFilter,然后将它下推到 Scan 节点 通用 通用性较好,适用于各种类型、效果也较好 配置比较复杂且计算成本较高;当过滤率较低或者左表数据较少时,可能导致性能降低 一些使用的注意事项(比较细节了,后面考虑结合代码再深入理解): 开启 Runtime Filter 后,左表的 ScanNode 会为每一个分配给自己的 Runtime Filter 等待一段时间再扫描数据,即如果 ScanNode 被分配了 3 个 Runtime Filter,那么它最多会等待 3000ms。 因为 Runtime Filter 的构建和合并均需要时间,ScanNode 会尝试将等待时间内到达的 Runtime Filter 下推到存储引擎,如果超过等待时间后,ScanNode 会使用已经到达的 Runtime Filter 直接开始扫描数据。 如果 Runtime Filter 在 ScanNode 开始扫描之后到达,则 ScanNode 不会将该 Runtime Filter 下推到存储引擎,而是对已经从存储引擎扫描上来的数据,在 ScanNode 上基于该 Runtime Filter 使用表达式过滤,之前已经扫描的数据则不会应用该 Runtime Filter,这样得到的中间数据规模会大于最优解,但可以避免严重的劣化。 如果集群比较繁忙,并且集群上有许多资源密集型或长耗时的查询,可以考虑增加等待时间,以避免复杂查询错过优化机会。如果集群负载较轻,并且集群上有许多只需要几秒的小查询,可以考虑减少等待时间,以避免每个查询增加 1s 的延迟。 Join Reorder 优化 有了前面两表 Join 的 Runtime Filter 铺垫,再来看 Join Reorder 的优化,逻辑关系上就能够理顺了。 Doris 目前的 Join Reorder 算法是基于 RBO 的,逻辑描述如下: 尽量让大表跟小表做 Join,它生成的中间结果是尽可能小的 把有条件的 Join 表往前放,也就是说尽量让有条件的 Join 表进行过滤 Hash Join 的优先级高于 Nest Loop Join,因为 Hash join 本身是比 Nest Loop Join 快很多的 可以发现前两条,都是在朝着让「右表」更小的方向去优化,而最后一条则是从算法的性能上来考虑。 Join 调优建议 Join 列最好是相同的简单类型;同类型避免 Cast 操作,简单类型则有不错的 Join 计算性能 Join 列最好是 Key 列,原因是 Key 列能够充分利用 Doris 延迟物化的特性,减少 IO 提升性能 大表之间的 Join 最好能够利用上 Colocate,相当于已经做好了预 Shuffle,实际查询的时候可以直接 Join 计算不再有 Shuffle 操作,彻底避免了大表的 Shuffle 网络开销 利用 Runtime Filter,Join 过滤性高时效果显著。根据 3 种 Runtime Filter 特点选择最适合的 涉及多表 Join,需要判断 Join 的合理性。尽量保证“左大右小”的原则,HashJoin 优于 NLJ。必要时可以通过 SQL Rewrite,通过 Hint 来调整 Join 顺序 REF https://www.jb51.net/article/266004.htm https://www.jb51.net/article/266000.htm (编辑:甘南站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
推荐文章
站长推荐
热点阅读