当前位置: 首页 > 科技观察

DorisJoin优化原理文档详解

时间:2023-03-12 22:50:03 科技观察

DorisJoin优化原理Doris支持两种物理算子,一种是HashJoin,一种是NestLoopJoin。HashJoin:在右表上基于等价的Join列建立哈希表,左表使用哈希表以流式的方式进行Join计算。它的局限性在于只能应用于等价的Join。NestLoopJoin:通过两个for循环,很直观。那么适用于不等值Join的场景,比如:大于小于或者需要笛卡尔积的场景。它是一个通用的Join运算符,但性能较差。作为分布式MPP数据库,Join过程中需要进行数据Shuffle。需要对数据进行拆分和调度,以保证最终的Join结果是正确的。举个简单的例子,假设关系S和R是join的,N表示参与join计算的节点数;T表示关系中元组的数量。DorisShuffle方法Doris支持4种Shuffle方法,BroadcastJoin,需要将右表的数据全部发送到左表,即每个参与Join的节点都有右表的全量数据,即,T(R)。它的适用场景比较通用,可以同时支持HashJoin和NestloopJoin,网络开销为N*T(R)。左表数据不动,右表数据发送到左表数据的扫描节点。ShuffleJoin在进行HashJoin时,可以通过Join列计算出对应的Hash值,进行Hashbucketing。它的网络开销是:T(R)+T(N),但是它只能支持HashJoin,因为它也是根据Join的条件计算buckets。左右表数据根据分区和计算的过失发送到不同的分区节点。BucketShuffleJoinDoris的表数据是通过Hash计算分桶的,所以可以利用表本身的桶列的性质对Join数据进行shuffle。如果两张表需要join,并且join列是左表的bucket列,那么实际上可以计算左表的数据,而不用移动右表的数据,通过数据的bucket发送数据在左表中。它的网络开销为:T(R)相当于只对右表的数据进行Shuffle。左表数据不动,右表数据根据分区计算结果发送到左表扫描节点Colocate。类似于BucketShuffleJoin,相当于导入数据时预设Join列的场景。数据洗牌。那么在实际查询时,可以直接进行join计算,无需考虑datashuffle问题。数据已经预先分区,Join计算在本地进行。四种Shuffle方法对比Shuffle方法。网络开销。物理算子适用场景BroadCastN*T(R)HashJoin/NestLoopJoingeneralShuffleT(S)+T(R)HashJoingeneral在BucketShuffleT(R)HashJoinJoin条件下有左表的分布式列,而左表在执行时是一个分区。Colocate0HashJoinJoin条件有左表的分布列,左右表属于同一个ColocateGroupN:参与Join计算的InstancenumberT(relationship):关系中的Tuples个数。以上四种方法的灵活性从高到低。它对这种数据分布的要求越来越严格,但是Join计算的性能也越来越好。.RuntimeFilterJoin优化Doris在进行HashJoin计算时会在右表上构建哈希表,左表流经右表上的哈希表得到Join结果。RuntimeFilter充分利用了右表的Hash表。当右表生成哈希表时,同时生成基于哈希表数据的过滤条件,然后下推到左表的数据扫描节点。这样,Doris就可以在运行时进行数据过滤。如果左表是大表,右表是小表,那么Join层要过滤的大部分数据,在读取数据的时候,可以通过右表生成的过滤条件,提前过滤掉。提高连接查询的性能。目前Doris支持三种类型的RuntimeFilter。一个是IN,很好理解,下推一个hashset到数据扫描节点。第二种是BloomFilter,利用哈希表中的数据构造BloomFilter,然后将BloomFilter下推到扫描节点进行数据查询。.最后一个是MinMax,就是一个Range区间。通过右表数据确定Range范围后,下推到数据扫描节点。RuntimeFilter的适用场景有两个要求:第一个要求是左表大,右表小,因为构建RuntimeFilter需要计算成本,包括一些内存开销。第二个需求是左右表join的结果很少,说明这个join可以过滤掉左表的大部分数据。当满足以上两个条件时,启用RuntimeFilter可以获得更好的效果。当Join列是左表的Key列时,RuntimeFilter会被下推到存储引擎。Doris本身支持延迟物化。延迟实现简单来说就是这样:如果需要扫描A、B、C三列,A列有一个过滤条件:A等于2,如果要扫描100行,可以先过滤列A扫描100行,然后通过A=2的过滤条件进行过滤,过滤完成后,读取B和C列,可以大大减少数据读取IO。所以如果在Key列上生成RuntimeFilter,同时利用Doris自身的延迟物化进一步提升查询的性能。RuntimeFilterTypeDoris提供了三种不同的RuntimeFilter类型:IN的优点是过滤效果明显且速度快。它的缺点一是只适用于BroadCast,二是当右表超过一定数据量时会失效。目前Doris当前配置的是1024,即如果右表大于1024,IN的RuntimeFilter会直接失效。MinMax的优点是开销比较小。它的缺点是对数值列效果较好,但对非数值列基本没有效果。BloomFilter的特点是通用,适合各种类型,效果比较好。缺点是其配置较复杂,计算量较高。一旦JoinReorder数据库涉及多表Join,Join的顺序对整个Join查询的性能影响很大。假设有3个表join,参考下图,左边是a表和b表的join,中间结果有2000行,再和c表join计算。接下来看右图,调整Join的顺序。先把a表和c表join,生成的中间结果只有100,最后再和b表join计算。最后的Join结果是一样的,但是它产生的中间结果有20次的差距,会产生很大的性能Diff。Doris目前支持基于规则的JoinReorder算法。它的逻辑是:让大表和小表尽可能join,它产生的中间结果尽可能小。把conditionalJoin表往前放,也就是尽量过滤conditionalJoin表。HashJoin的优先级高于NestLoopJoin,因为HashJoin本身比NestLoopJoin快很多。DorisJoin调优方法DorisJoin调优方法:利用Doris自身提供的Profile来定位查询的瓶颈。Profile会记录Doris整个查询过程中的各种信息,这些信息是性能调优的第一手资料。.理解Doris的Join机制,这也是第二部分给大家分享的内容。只有知其然,知其所以然,了解其机理,才能分析出为什么会慢。使用Session变量改变Join的一些行为,实现Join的调优。查看QueryPlan分析这个调优是否生效。以上4步基本完成了一个标准的Join调参流程,接下来就是实际查询验证,看看效果如何。如果把前面4种方法串联起来,还是不行。这时候可能需要重写Join语句,或者调整数据分布。需要重新检查整个数据分布是否合理,包括查询Join语句。可能需要进行一些手动调整。当然,这种方法的心理成本比较高,也就是说,只有在尝试前面的方法都不行的情况下,才需要做进一步的分析。调优案例实际案例一:四张表的连接查询。通过profile的时候,发现第二次join的时间非常长,需要14秒。Profile进一步分析,发现BuildRows,即右表的数据量约为2500万。而ProbeRows(ProbeRows是左表的数据量)只有10000多条。在这种场景下,右表比左表大很多,这显然是一种不合理的情况。这明显说明Join的顺序有问题。这时候尝试改变Session变量,开启JoinReorder。设置enable_cost_based_join_reorder=true