山大软工实践hive(6)-逻辑优化(2)

2021SC@SDUSC

山大软工实践hive(6)-逻辑优化(2)

以下接着上一次

GroupBy

预聚合

在map端使用combiner预聚合数据以减少shuffle数据量,这对大量数据处理有帮助

数据倾斜处理

为防止大量数据的key相同导致分配任务时的数据倾斜,两阶段聚合数据
原理是启动两个MapReduce任务,第一个任务mapper随机分发给reducer数据,然后reducer做聚合,然后第二MR任务就对预处理过的做聚合
对应的优化器为GroupByOptimizer

关于预聚合能否用于处理数据倾斜

值得注意的是,预聚合用Combiner实现。而关于它:
Combiner只应该用于那种Reduce的输入key/value与输出key/value类型完全一致,且不影响最终结果的场景。比如累加,最大/小值等。所以预聚合本身就无法随时使用,只有是如sum,count函数时才能运用。
Combiner最基本是实现本地key的归并,Combiner具有类似本地的reduce功能;Combiner的实现一般继承Reducer。
Combiner在Mapper之后Reducer之前运行。

虽然预聚合理论上能够用于处理数据倾斜,但它本身也是继承Reducer的一种实现,所以从概念上讲,数据倾斜处理本身是包含预聚合的

join优化

1.小表做hash表读入内存,可用于map join与Bucket map join(mapjoin会在map阶段完成join操作,不需要reduce)
2.多张表join时key相同,则会合并为一个MR任务,相关性优化器CorrelationOptimizer.

数据倾斜处理

1.Hive将计数超过阈值的倾斜key对应的行临时写进文件中,再启动map join,其它数据用common join,这就是skew join(创建表时指定某属性倾斜key)
2.优化HQL以解决倾斜

  • 对于空值或者无意义的值,可以把它随机变成不可能的值

  • 对于倾斜key可以将它们抽样出来,对应的行单独存入临时表中,key打上前缀如0-9,最后再聚合

  • 不同数据类型之间,如int join string,会让其以int做hash,所有string类型key被分配在一个reducer上(应该是把所有string类型认为是相同key了吧),所以写sql时可以先把数据类型进行转换

  • map join小表太大,对小表做出限制筛选,但要两次map join,比如下面HQL(微改动了下原文语句)

     select /*+mapjoin(b)*/ a.uid
     from log a left outer join 
     (select /*+mapjoin(s)*/ t.uid
     from (select distinct uid from log where pt_date = 20190228) s 
     inner join user_info t on s.uid = t.uid) b on a.uid = b.uid 
     where a.pt_date = 20190228;
    

    大概就是日志表和用户信息表join,获得当天进行某操作的用户数据,一个用户可以用多条log记录,所以先对log表去重地让获取当天的用户id(表s),然后与用户信息表join获取当天操作过的用户信息(表b),最后用log表(表a)与表b join
    这里user_info 就是小表,但它不够小,所以要先筛选一次,总的来说,这种做法,进行了两次map join

MapReduce优化

mapper数

mapper数与文件分割数相关,在org.apache.hadoop.mapreduce.lib.input.FileInputFormat类可以看到相关逻辑
在这里插入图片描述
有参数(简化名字后)tasks, default_num, block_size, input_size, split_num, split_size, min_split, max_split, 以及最终的mapper_num,它们的关系如下

  • default_num=input_num/block_size(文件大小除以块大小,块大小默认值为为64/128MB)
  • split_size=MAX(min_split,MIN(max_split,block_size)),如果block_size在区间最小与最大值之间,则为block_size,否则为最小或最大值(min_split/max_split 默认值为1KB/64MB)。与上面类似,split_num=input_size/split_size
  • mapper_num=MIN(split_num, MAX(default_num, tasks)) 其中tasks为期望值,它只有在数量合适时会采用。可以看见调高max_split更可能会使mapper数减少(原文是调高min_split),调高tasks与max_split更可能增大mapper数(原文还是min_split)

输入:少量大文件则减少mapper数,大量文件则增加,大量小文件需要合并小文件

调整Reducer数

相关tasks参数能直接指定reducer数,而非建议,如果不启用,则按照 reducer_num=MIN(输入大小/reducer默认大小,reducer数的最大值)

JVM复用

MR任务中,默认每启动一个task就启动一个JVM,而如果task多而小,则会浪费开销在JVM启动与关闭。可以通过设定相关参数tasks为n,代表在同一MR任务中顺序执行的n个task可以重用一个JVM

并行与本地与严格执行

并行:如union语句前后可以并行执行
本地:如果任务量小可以直接在节点完成任务,而不提交到集群
严格执行:禁止使用高风险HQL

  1. 查询分区表时不限定分区列的语句
  2. 两表join产生了笛卡尔积的语句
  3. 用order by来排序但没有指定limit的语句(前面有提到order by会导致任务分给一个Reducer,如果不限制数量则很可能负载过大)

看一看源码

对于提到的两个优化器
GroupByOptimizer
在这里插入图片描述
进行group by优化,如果gb key包含bucket,sorting key,则可以直接join(SMBjoin,第4篇提到过,可以猜测此情况会产生SMB算子替代原join->rs->join)

CorrelationOptimizer
在这里插入图片描述
这个优化器的实现参考了一篇论文
让我注意的是,这里的query plan tree似乎与operator tree(DAG)相同,我以后可以认为这是一个东西

我又想到Transform
在这里插入图片描述
所有基于规则的优化都实现了这个接口。所有转换都是按顺序调用的。它们获取当前解析上下文(其中包含操作符树等),执行所有优化,然后返回更新的解析上下文。transform方法被所有继承类继承
这突然让我想到,我不必去找谁在调用了,反正估计Optimizer类initialize方法应该是调用优化器的方法的根,可以从这里把所有的优化器类根据注释与transform方法等把它们的功能罗列一遍,看一看情况

碎语

目前没找到那种对DAG调整的逻辑优化的博客,同时也不知道这些属不属于DAG调整部分,而且这看的源码的很多优化器还没有找到对应的理论。
这写着和转载有什么区别,希望有一个‘’总结‘’分类。走着看吧。。。。

下一篇决定先去翻一翻所有带在Optimizer类initialize方法里带Optimizer结尾的类(不带的视情况而定),应该能确认一些东西

主要参考

hive10种优化
两阶段聚合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值