文中转载微信公众平台「五分钟学互联网大数据」,创作者园陌 。转截文中请联络五分钟学互联网大数据微信公众号。
数据倾斜难题分析
数据倾斜是分布式架构难以避免的难题,一切分布式架构都是有概率产生数据倾斜,但有一些小伙伴们在平常工作上认知并不是很显著。这儿要留意这篇文章内容的文章标题—“千亿元级数据信息”,为什么说千亿元级,由于假如一个每日任务的信息量仅有上百万,它即便发生了数据倾斜,全部数据信息都跑到一台设备去实行,针对上百万的信息量,一台设备实行起來或是没什么工作压力的,这时候数据倾斜对大家认知并不大,仅有数据信息做到一个数量级时,一台设备应付账款不上这么多数据信息,这时候假如产生数据倾斜,最终就难以算出結果。
因此 就必须大家对数据倾斜的难题开展提升,尽量减少或缓解数据倾斜产生的危害。
在处理数据倾斜难题以前,也要再提一句:沒有短板时讨论提升,全是怨天尤人。
大伙儿想一想,在map和reduce两个阶段中,最非常容易发生数据倾斜的便是reduce环节,由于map到reduce会历经shuffle环节,在shuffle中默认设置会依照key开展hash,假如同样的key太多,那麼hash的結果便是很多同样的key进到到同一个reduce中,造成 数据倾斜。
那麼是否有很有可能在map环节就产生数据倾斜呢,是有这类很有可能的。
一个每日任务中,数据库文件在进到map环节以前会开展分割,默认设置是128M一个数据信息块,可是假如当对文档应用GZIP缩小等不兼容文件分割实际操作的压缩方式时,MR每日任务载入缩小后的文档时,是对它分割不上的,该压缩包总是被一个每日任务所载入,假如有一个超大型的不能分割的压缩包被一个map载入时,便会产生map环节的数据倾斜。
因此 ,从实质上而言,产生数据倾斜的缘故有二种:一是每日任务中必须解决很多同样的key的数据信息。二是每日任务载入不可缺少的大文件。
数据倾斜解决方法
MapReduce和Spark中的数据倾斜解决方法基本原理全是相近的,下列探讨Hive应用MapReduce模块引起的数据倾斜,Spark数据倾斜还可以此为参考。
1. 空值引起的数据倾斜
具体业务流程中有一些很多的null值或是一些无意义的数据信息参加到测算工作中,表格中有很多的null值,假如表中间开展join实际操作,便会有shuffle造成,那样全部的null值都是会被分派到一个reduce中,必定造成数据倾斜。
以前有小伙伴们问,假如A、B两表join实际操作,倘若A表格中必须join的字段名为null,可是B表格中必须join的字段名不以null,这两个字段名压根就join不上啊,为何还会继续放进一个reduce中呢?
这儿大家必须确立一个定义,数据信息放进同一个reduce中的缘故并不是由于字段名能否join上,只是由于shuffle环节的hash实际操作,只需key的hash結果是一样的,他们便会被拖到同一个reduce中。
解决方法:
第一种:能够立即不许null值参加join实际操作,即不许null值有shuffle环节
- SELECT *
- FROM log a
- JOIN users b
- ON a.user_id IS NOT NULL
- AND a.user_id = b.user_id
- UNION ALL
- SELECT *
- FROM log a
- WHERE a.user_id IS NULL;
第二种:由于null值参加shuffle时的hash結果是一样的,那麼我们可以给null值任意取值,那样他们的hash結果就不一样,便会进到不一样的reduce中:
- SELECT *
- FROM log a
- LEFT JOIN users b ON CASE
- WHEN a.user_id IS NULL THEN concat('hive_', rand())
- ELSE a.user_id
- END = b.user_id;
2. 不一样基本数据类型引起的数据倾斜
针对2个表join,表a中必须join的字段名key为int,表b中key字段名不仅有string种类也是有int种类。当依照key开展2个表的join实际操作时,默认设置的Hash实际操作会按int型的id来开展分派,那样全部的string种类都被分派成同一个id,結果便是全部的string种类的字段名进到到一个reduce中,引起数据倾斜。
解决方法:
假如key字段名不仅有string种类也是有int类型,默认设置的hash就都是会按int类型来分派,那大家立即把int类型都变为string就好了,那样key字段名都为string,hash时就依照string种类分派了:
- SELECT *
- FROM users a
- LEFT JOIN logs b ON a.usr_id = CAST(b.user_id AS string);
3. 不能分拆大文件引起的数据倾斜
当群集的信息量提高到一定经营规模,有一些数据信息必须存档或是数据归档,此刻通常会对数据信息开展缩小;当对文档应用GZIP缩小等不兼容文件分割实际操作的压缩方式,在日后有工作涉及到载入缩小后的文档时,该压缩包总是被一个每日任务所载入。假如该压缩包非常大,则解决该文件的Map必须耗费的時间会远超过载入一般文档的Map時间,该Map每日任务会变成工作运作的短板。这类状况也就是Map读取文件的数据倾斜。
解决方法:
这类数据倾斜难题沒有哪些好的解决方法,只有将应用GZIP缩小等不兼容文件分割的文档变为bzip和zip等适用文件分割的压缩方式。
因此 ,我们在对文档开展缩小时,为防止因不能分拆大文件而引起数据信息载入的歪斜,在数据编码的情况下能够选用bzip2和Zip等适用文件分割的压缩算法。
4. 数据信息澎涨引起的数据倾斜
在多维汇聚测算时,假如开展排序汇聚的字段名太多,以下:
select a,b,c,count(1)from log group by a,b,c with rollup;
注:针对最终的with rollup关键词不清楚大伙儿使用过没,with rollup是用于在排序数据统计的基本上再开展统计分析归纳,即用于获得group by的归纳信息内容。
假如上边的log表的信息量非常大,而且Map端汇聚不可以非常好地具有数据编码的状况下,会造成 Map端产出率的数据信息极速澎涨,这类状况非常容易造成 工作内存溢出的出现异常。假如log表带有数据倾斜key,会加重Shuffle全过程的数据倾斜。
解决方法:
能够分拆上边的sql,将with rollup拆分为以下好多个sql:
- SELECT a, b, c, COUNT(1)
- FROM log
- GROUP BY a, b, c;
- SELECT a, b, NULL, COUNT(1)
- FROM log
- GROUP BY a, b;
- SELECT a, NULL, NULL, COUNT(1)
- FROM log
- GROUP BY a;
- SELECT NULL, NULL, NULL, COUNT(1)
- FROM log;
可是,上边这类方法不大好,由于现在是对3个字段名开展排序汇聚,那如果是五个或是10个字段名呢,那麼必须拆卸的SQL句子会大量。
在Hive中能够根据主要参数 hive.new.job.grouping.set.cardinality 配备的方法自动控制系统工作的拆卸,该主要参数初始值是30。表明对于grouping sets/rollups/cubes这类多维汇聚的实际操作,假如最终拆卸的键组成超过该值,会开启新的每日任务去解决超过该值以外的组成。假如在解决数据信息时,某一排序汇聚的列有很大的歪斜,能够适度调小该值。
5. 表联接时引起的数据倾斜
两表开展一般的repartition join时,假如表联接的键存有歪斜,那麼在 Shuffle 环节必定会造成数据倾斜。
解决方法:
一般作法是将歪斜的数据信息存到分布式缓存中,派发到每个Map每日任务所属连接点。在Map环节进行join实际操作,即MapJoin,这防止了 Shuffle,进而防止了数据倾斜。
MapJoin是Hive的一种提升实际操作,其适用小表JOIN大表的情景,因为表的JOIN实际操作是在Map端且在运行内存开展的,因此 其并不一定运行Reduce每日任务也就不用历经shuffle环节,进而能在一定水平上节约資源提升JOIN高效率。
在Hive 0.11版本号以前,假如想在Map环节进行join实际操作,务必应用MAPJOIN来标识表明地运行该提升实际操作,因为其必须将小表载入进运行内存因此 要留意小表的尺寸。
如将a表放进Map端运行内存中实行,在Hive 0.11版本号以前必须那样写:
- select /* mapjoin(a) */ a.id , a.name, b.age
- from a join b
- on a.id = b.id;
假如想将好几个表放进Map端运行内存中,只需在mapjoin()中写好几个表名字就可以,用分号隔开,如将a表和c表放进Map端运行内存中,则 /* mapjoin(a,c) */ 。
在Hive 0.11版本号及以后,Hive默认设置运行该提升,也就是没有必须表明的应用MAPJOIN标识,其会在必需的情况下开启该提升实际操作将一般JOIN转化成MapJoin,能够根据下列2个特性来设定该提升的开启机会:
hive.auto.convert.join=true 初始值为true,全自动打开MAPJOIN提升。
hive.mapjoin.smalltable.filesize=2500000 初始值为2500000(25M),根据配备该特性来明确应用该提升的表的尺寸,假如表的尺寸低于此值便会被载入进运行内存中。
留意:应用默认设置运行该提升的方法假如发生无缘无故的BUG(例如MAPJOIN并失灵),就将下列2个特性置为fase手动式应用MAPJOIN标识来运行该提升:
hive.auto.convert.join=false (关掉全自动MAPJOIN变换实际操作)
hive.ignore.mapjoin.hint=false (不忽视MAPJOIN标识)
再提一句:将表放进Map端运行内存时,假如连接点的运行内存非常大,但或是发生内存溢出的状况,我们可以根据这一主要参数 mapreduce.map.memory.mb 调整Map端运行内存的尺寸。
6. 的确没法降低信息量引起的数据倾斜
在一些实际操作中,大家没有办法降低信息量,如在应用 collect_list 涵数时:
- select s_age,collect_list(s_score) list_score
- from student
- group by s_age
collect_list:将排序中的某列变为一个二维数组回到。
在以上sql中,s_age假如存有数据倾斜,当信息量大到一定的总数,会造成 解决歪斜的reduce每日任务造成内存溢出的出现异常。
注:collect_list輸出一个二维数组,正中间結果会放进运行内存中,因此 假如collect_list汇聚过多数据信息,会造成 内存溢出。
有小伙伴们说它是 group by 排序造成的数据倾斜,能够打开hive.groupby.skewindata主要参数来提升。大家下面剖析下:
打开该配备会将工作拆卸成2个工作,第一个工作会尽量将Map的数据信息平分到Reduce环节,并在这个环节完成数据信息的预汇聚,以降低第二个作业处理的信息量;第二个工作在第一个作业处理的数据信息基本上开展結果的汇聚。
hive.groupby.skewindata的带头作用取决于转化成的第一个工作可以合理降低总数。可是针对collect_list这类规定全量实际操作全部数据信息的正中间結果的涵数而言,显著起不上功效,反倒由于引进新的工作提升了硬盘和互联网I/O的压力,而造成 特性越来越更加不高。
解决方法:
这类难题最立即的方法便是调节reduce所实行的内存空间。
调节reduce的内存空间应用mapreduce.reduce.memory.mb这一配备。
【责编:武晓燕 TEL:(010)68476606】