一、关于hive调优
行列过滤
行列过滤的目的是什么?拿出尽量少的数据,一方面节省IO,另外如果后面需要用到join等操作,也是提高后面操作的效率
如何进行行过滤?拿需要的列(避免select*)和需要的行(提前where过滤)
hive的列式存储本就是为了什么?方便列过滤,列式存储单独拿某几列的效率是很高的
hive为什么要做成分区表?方便行过滤,分区的时候行过滤会变得更容易,直接按所需的文件取就完事,省去扫描全表再取所需的行的过程
2. 合理设置map和reduce的数量
如何合理设置map数量?首先小文件切片默认自动合并成大的切片可减少map数量,其次功能复杂的map可适当减少一个切片的最大值参数来间接增加Map数量,减少数量反之
如何合理设置reduce数量?Reduce个数并不是越多越好,首先初始化消耗资源,其次小文件输出,因此我们将reduce时列级别统计参数设为true,让其估算值更加准确,如果有特殊需要,比如我就是想让输出文件为1时,可以将reduce个数直接设置为1,或打开reduce的输出合并
如何不让程序走reduce?采用MapJoin,其不同于common join(shuffle join、reduce join),在Map端就完成Join,默认是打开的,他通过map端缓存一张小表的数据从而比对另一张大表的数据来完成join,这样的好处是既不走reduce,没有shuffle和IO,又间接解决了数据倾斜的问题,而具体小表有多小,由参数hive.mapjoin.smalltable.filesize来决定,该参数表示小表的总大小,默认值为25000000字节,即25M。但是要注意的是,left join时只能小表是右表,因为要大表作为驱动表切分map然后分别拿小表所生成的hash表做比对连接,而如果小表为左表作为驱动表是没法进行处理的
3. 小文件如何产生的以及其危害?
1.危害:
(1)存储层面:每个文件块都会占用namenode150字节存储元信息,危害NameNode
(2)计算层面:每个小文件都会起到一个MapTask,一个MapTask默认资源1G,浪费资源
2.如何产生:
(1)动态分区插入数据,产生大量的小文件(map输出)->危害存储
动态分区为什么会map输出而不是reduce输出?参考一下上面所述的mapjoin
但这种小文件需要合并吗?不需要,一般都是一个分区里一个小文件,总不能吧不同分区的合并一起吧
(2)数据源本身就包含大量的小文件(map输入);->危害计算
(3)reduce数量越多,小文件也越多(reduce输出)。->危害存储
3.解决方案:
(1)对于上述1,我们采用har归档方式,将小文件归档,多个原信息只存储一份
(2)对于上述2,采用在Map执行前合并小文件,减少Map数,CombineHiveInputFormat系统默认打开;或开启JVM重用,两者2选1,因为如果已经合并小文件,但仍然jvm重用意义不大,会一直占用使用到的task卡槽,直到任务完成才释放
(3)对于上述3,采用输出合并小文件merge
4.其他优化
压缩(选择快的),设置map端输入、中间结果压缩
(1)map端输入:如果数据量小,速度优先,则选择snappy;如果数据量大,切片优先,则选择bizp2,lzop
(2)map端输出:如果后面有reduce,速度优先,因为reduce不需要切片,选择snappy,如果后面无reduce,map端直接输出,同时直接输出也要看需求,如果后面有其他任务要使用该数据,则遵循3
(3)reduce端输出:如果后面有下一个map,或者有其他任务要使用该数据,则遵循1,如果没有则永久保存,此时采用压缩比高的gzip等节省空间
采用tez引擎或者spark引擎
因为spark引擎对mr进行了优化,按stage进行切分,如果原本任务(例如需要一次mr的任务但最后需要对reduce端小文件进行合并)即map->reduce->map->reduce,当使用spark时,变为stage(map)->stage(reduce)->stage(reduce),中间会减少一次map阶段,这样虽不会减少一次shuffle,还是两次shuffle(shuffle次数为stage个数-1),但是减少了一次map,也就意味着减少一次磁盘IO,而当需要多个mr串联起来的任务,减少的就不止一次map和IO了
采用combine预聚合
原理:map端每次溢写文件时,都会进行合并排序,同时不同的溢写文件合并成大文件时相同分区之间,也会合并排序,而在合并排序的过程中,是可以顺便完成预聚合功能的,而提前聚合就会导致数据量少,数据量少节省IO,同时间接解决了数据倾斜,因为原本每个map里可能都有很多个北京,现在是每个map溢出的大文件里只有一份北京,这样最后reduce去操作的时候,北京的数量就不会超过map数量了,是可以接受的
注意:使用时要在不影响最终业务逻辑的前提下,可以累加但不要牵扯到除法运算
二、Hive数据倾斜
怎么产生的数据倾斜?
首先有一个大前提,那就是你的reduce数量至少为2个,如果一个reduce无所谓倾斜与否
(1)单表如何产生数据倾斜?单表产生倾斜往往是由于group by产生,因为分组聚合会进入reduce阶段,而如果某个字段值数量较多,则会被hash分到同一个reduce分区中,而其他分区早就处理完了但由于该分区有较大数据量,拖延整体任务,因此造成倾斜
(2)多表如何产生数据倾斜?多表产生倾斜往往是由于join on产生,倘若关联字段某个值数据量较大,则被分到的reduce分区就会承担更大的压力,因此造成数据倾斜,而如果这两个表的关联字段类型又不相同,则会更加加重这种倾斜效果,比如用户表中user_id字段为int,log表中user_id字段string类型,如果让他们在底层自己强转,则速度会更慢,而如果我们提前在reduce前强转,则可以适当缓解这种倾斜效果,我们要把字符串类型转换成数字类型 on a.usr_id = cast(b.user_id as int)
(3)多表数据倾斜就不会是由于group by了吗?当然可能,假如我们在Join on之前就对其他字段或关联字段进行了group by,则这里面也是会发生数据倾斜的,或者在我们在join on之后又对其其他字段或关联字段进行了group by,而如果我们前后操作的都是关联字段,是会多处倾斜的
(4)空值也会发生倾斜吗?如果发生空值数量过多,多个空值则会发往同一个reduce从而造成数据倾斜,在处理空值产生会给你的数据倾斜和其他值的处理是一样的方法,毕竟他也可以认为是某个值倾斜
2.对于group by产生的数据倾斜如何处理?
(1)对于group by, 首先能进行预聚合的情况下我们首先考虑预聚合
(2)其次我们可以采用双重聚合,即加随机数聚合,set hive.groupby.skewindata = true, 其原理是将原本数量多的例如北京,变成北京1和北京2,这时他们就会分入不同的reduce,从而一定程度减少倾斜,但由于最后还是要把北京1和2聚合在同一组,因此仍需要把他们两者的结果再次聚合在一起,因此成为双重聚合,也叫做skew group by,由于此方法要使用两次reduce,也就是要经过两次shuffle过程,还是不如预聚合的
(3)再其次我们还可以使用自定义分区器,将倾斜值放入单独空闲的reducer,因为本来北京就倾斜,你还将他和其他省份hash入同一个reduce处理,只会让其更加倾斜,因此把他单独拿出来处理,或许会好一些,但仍然会有一定的倾斜,因此也不是特别推荐
(4)增加reduce个数,也就是增加分区数,这样北京就会和更少的城市分到一个分区了,也是对方式3的一种暴力替代,但也是仍然会有一定倾斜,但如果多个值同时倾斜的时候,这种处理方式或许会奏效,试想一下,如果原本北京上海深圳等都在同一个分区里,但现在增加分区数,他们被分往不同分区了,是不是就一定程度解决了数据倾斜呢?但由于这样做浪费资源,而且有运气成分存在,因此也不是完全推荐
对于join on产生的数据倾斜如何处理?
大表JOIN小表:
(1) map Join默认打开,直接避免了Reducer,自然不存在倾斜
大表JOIN大表:
(1) skew join,也是变相的Mapjoin,原理和skew group by的加随机数没有联系,其通过将倾斜值统计,如果超过某个阈值,则将该值的数据放入文件中,同时将另一张的表该值做同样处理,这时候我们就产生了两张表,而这两张大表经过这样裁剪产生的表大小不足够大,因此就可以放在内存里进行map端的join了,虽然倾斜值不再需要走reduce端,但中间仍然有IO落盘操作,而且只能适用于inner join,一般不选择
(2)分桶join,仍然是变相的Mapjoin,原理是将两张表作为分桶表,且分桶字段为链接字段,那么这个时候就不需要整体链接做比对了,而是分桶作比对即可,此时的小表是一个个分桶,而不是整张表了,相当于做了多个mapjoin,但是这里要注意的是,大表必须是小表桶的整数倍,因为这样才可以hash以后刚好放入能匹配上的桶,而不会出现等值却不在同一个桶的交叉现象,而且最好将桶数量设置为2的整数倍,方便所有的桶不管哪两个相链接,都可以完成分桶join
(3)A表加随机数B表扩容,该方法类似于skew group by的加随机数,A表所有数据加随机数以后,B表扩容成多份,每份加上不同的随机数,这时,相同随机数的例如北京1和北京1会落入同一个reduce中,北京2和北京2落入另一个中,以此来防止数据倾斜,但需要注意的是,到最后,A表的北京1不需要再和B表的北京2再次链接,因为B表的北京2只是一个备份而已,北京1已经和同样数据的北京1完成了链接