引语
上一篇介绍了关于Hive优化的一些基本概念,这一篇主要讲hive性能优化的一些具体事项,这篇主要将对数据倾斜问题的优化,以及其他的方面的一些优化。数据倾斜
什么是数据倾斜
在Hadoop当项目中,数据倾斜可以说是损害Hadoop性能的罪魁祸首。在运行Hadoop的任务过程当中,我们可能因为业务的需要,避免不了需要按照某个字段分组,去重,进行多表连接等操作,在这些操作当中一旦有些使用不当,就会很容易造成数据倾斜。同样,当一个模型设计不合理时,job数量指定过多或者分区不恰当时也很容易影响Hadoop的性能。那么什么是数据倾斜?怎样避免或者处理数据倾斜问题呢?接下来就为大家介绍具体的关于解决数据倾斜问题的具体方案。
数据倾斜就是key分布不均匀,导致分发到不同的reduce上,个别reduce任务特别重,导致其他reduce都完成,而这些个别的reduce迟迟不完成的情况 。
数据倾斜的原因
key分布不均匀。
map端数据倾斜,输入文件太多且大小不一 。
reduce端数据倾斜,分区器问题。
业务数据本身的特征。
数据倾斜的症状
任务长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。
查看子任务,可以看到本地读写数据量积累非常大,通常超过10GB可以认定发生了数据倾斜。
平均记录数超过50w且最大记录数超过平均记录数的4配
最长时长超过平均时长4分钟,且最大时长超过平均时长的2倍
一般来说集群的整体性能,是由执行任务时间最长的那个节点决定的。所以,有效地解决数据倾斜问题,就能有效地提高整个集群的性能。接下来我们来看一下具体的解决方法吧。
数据倾斜—大大表关联
有时join超时是因为某些key对应的数据太多,而相同key对应的数据都会发送到相同的reducer上,从而导致内存不够。此时我们应该仔细分析这些异常的key,很多情况下,这些key对应的数据是异常数据,我们需要在SQL语句中进行过滤。同时当有筛选条件时,应该先按照条件进行过滤,然后在进行join,这样可以减少数据量。
数据倾斜—大小表关联
原因:Hive在进行join时是以左边的表为准的,在join左边的表的数据会首先读入内存,如果左边的表key比较集中,而表的数据量又比较大,这样就会导致加入内存的数据过大,最终就会出现严重的数据倾斜。相反如果左边的表,key相对分散,那么读入内存的数据会比较小,join任务执行会比较快,就不会出现数据倾斜的现象。
解决思路:
将key相对分散,并且将数据量小的表放在join的左边,这样可以有效减少内存溢出错误发生的几率。
使用map join让小的维度表先进内存。使用map join,会将其中做连接的小表(全量数据)分发到所有 MapTask 端进行 Join,从 而避免reduceTask,前提要求是内存足以装下该全量数据。
数据倾斜—聚合时存在大量特殊值
原因:做count distinct时,该字段存在大量值为NULL或空的记录。
思路:count distinct时,将值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结果中加1。
如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union。
其他hive优化
Hive的并行执行
同步执行hive的多个阶段,hive在执行过程,将一个查询转化成一个或者多个阶段。某个特定的job可能包含众多的阶段,而这些阶段可能并非完全相互依赖的,也就是说可以并行执行的,这样可能使得整个job的执行时间缩短。hive执行开启:set hive.exec.parallel=true
设置本地运行
Hive 在集群上查询时,默认是在集群上N台机器上运行, 需要多个机器进行协调运行,这个方式很好地解决了大数据量的查询问题。但是当 Hive 查询处理的数据量比较小时,其实没有必要启动分布式模式去执行,因为以分布式方式执行就涉及到跨网络传输、多节点协调 等,并且消耗资源。这个时间可以只使用本地模式来执行 mapreduce job,只在一台机器上执行,速度会很快。启动本地模式可以通过配置参数来达到这个目的:
参数名 | 默认值 | 作用 |
---|---|---|
set hive.exec.mode.local.auto | false | 是否让hive在本地运行 |
hive.exec.mode.local.auto.input.files.max | 4 | 是否启用本地模式的task的最大个数 |
hive.exec.mode.local.auto.inputbytes.max | 128M | 是否启动本地模式最大文件输入大小 |
参数说明:
set hive.exec.mode.local.auto=true 开启本地mr,只是打开 hive 自动判断是否启动本地模式的开关,也就是打开这个参数并不能保证启动本地模式,是否启动还要取决于 map 任务数量。
set hive.exec.mode.local.auto.input.files.max 设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式,默认为4。
hive.exec.mode.local.auto.inputbytes.max 当指定大小时,才会启动本地模式,不指定则默认不启动。 如果当输入文件大小小于此阈值时可以自动在本地模式运行,默认是 128兆。
严格模式
Hive中提供有严格模式,为了防止一些查询。出现不好的影响。例如笛卡儿积,在严格模式下是不能运行的。配置如下:<property>
<name>hive.mapred.mode</name>
<value>strict</value>
</property>
说明
默认值为:非严格模式 nonstrict
开启严格模式: strict
开启了严格模式,会对查询语句进行一些限制:
1 对于分区表: 必须存在where语句对分区表中分区字段进行条件过滤,否则,不允许执行该查询。
2 对于使用order by的语句必须使用limit 进行限定,由于order by 之后所有的数据都会被分到一个reduce中那这样reduce操作的数据量太多了,可能时间过长卡死。所以为了防止reduce时间过长,在order by的时候必须给定 limit 减少redue处理的数据量。
3 限制了笛卡儿积的查询,主要在多表join中会出现。笛卡儿积的出现会造成性能极大的消耗。
推测执行
在Hadoop中,作业完成时间取决于最慢的任务完成时间。一个作业由若干个Map任务和Reduce任务构成。因集群中的资源分配不均等、硬件老化、软件Bug等,会导致某个任务运行的时间快或者某个任务运行的时间慢,或者某个任务在运行的时候直接卡死了。;为了防止某些任务,在运行过程中,拖慢了整个MR任务的进度。在运行慢的任务节点上开启相同的任务,如果时间比原来的任务运行的快则直接输出推测运行的任务 。
典型案例:系统中有99%的Map任务都完成了,只有少数几个Map老是进度很慢,完不成,怎么办?
推测执行机制
发现拖后腿的任务,比如某个任务运行速度远慢于任务平均速度。为拖后腿任务启动一个备份任务,同时运行。谁先运行完,则采用谁的结果。
执行推测任务的前提条件
(1)每个task只能有一个备份任务;
(2)当前job已完成的task必须不小于0.05(5%)
(3)开启推测执行参数设置。Hadoop2.7.2 mapred-site.xml文件中默认是打开的。
<property>
<name>mapreduce.map.speculative</name>
<value>true</value>
<description>If true, then multiple instances of some map tasks may be executed in parallel.
</description>
</property>
<property>
<name>mapreduce.reduce.speculative</name>
<value>true</value>
<description>
If true, then multiple instances of some reduce tasks may be executed in parallel.
</description>
</property>
mapred.map.tasks.speculative.execution boolean true 如果任务运行变慢,该属性决定了是否要启动一个map任务的另外一个实力
mapred.reduce.tasks.speculative.execution boolean true 如果任务运行变慢,该属性决定这是否需要启动一个reduce任务
执行计划
Hive中提供的可以查看Hql语句的执行计划,在执行计划中会生成抽象语法树,在语法树中会显示HQL语句之间的依赖关系以及执行过程。通过这些执行的过程和依赖可以对HQL语句进行优化JVM重用
在hive执行计算任务的时候,会把的执行计划上传到yarn集群中进行提交,运行MR任务。每次进行任务的运行的时候都会开启一个JVM进程运行的MR任务。如果提交任务频繁过多就会造成JVM频繁的开启和关闭,正常情况下,MapReduce启动的JVM在完成一个task之后就退出了,但是如果任务花费时间很短,又要多次启动JVM的情况下,JVM的启动时间就会变成一个比较大的overhead。
在这种情况下,可以使用jvm重用的参数:set Mapred.Job.reuse.jvm.num.tasks = 5; 。其作用是让一个jvm运行多次任务之后再退出,节约JVM的启动时间。
小文件的合并
大量的小文件导致文件数目过多,例如当一个文件是130MB的时候,Hadoop会将这个文件分成2个默认块,一个是128MB剩余的分一个2MB另外分一个,这样就会给HDFS带来压力,对hive处理的效率影响比较大,对于小文件问题有两种处理方法。
设置hive参数,将额外启动一个MR Job打包小文件
hive.merge.mapfiles=true(默认值为真) 是否合并Map输出文件
hive.merge.mapredfiles=false(默认值为假)是否合并Reduce 端输出文件
hive.merge.size.per.task=256*1000*1000(默认值为 256000000)合并文件的大小
使用Combinefileinputformat,将多个小文件打包作为一个整体的inputsplit,减少map任务数。前面三个参数确定合并文件块的大小,大于文件块大小128m的,按照128m来分隔,小于128m,大于100m的,按照100m来分隔,把那些小于100m的(包括小文件和分隔大文件剩下的)进行合并
set mapred.max.split.size=100000000;
set mapred.min.split.size.per.node=100000000
set Mapred.min.split.size.per.rack=100000000
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat
合理利用文件存储格式
创建表时,尽量使用 orc、parquet 这些列式存储格式,因为列式存储的表,每一列的数据在物理上是存储在一起的,Hive查询时会只遍历需要列数据,大大减少处理的数据量。同时由于parquet文件是以二进制存储的,因此可以减少文件的序列化和法序列化的操作,从而节省时间。
对文件进行适当的压缩
Hive 最终是转为 MapReduce 程序来执行的,而MapReduce 的性能瓶颈在于网络 IO 和 磁盘 IO,要解决性能瓶颈,最主要的是减少数据量,对数据进行压缩是个好的方式。压缩 虽然是减少了数据量,但是压缩过程要消耗CPU的,但是在Hadoop中, 往往性能瓶颈不在于CPU,CPU压力并不大,所以压缩充分利用了比较空闲的 CPU。
减少Job的数量
对于一个job能够完成的任务,尽量不要使用两个job来完成,从而减少job初始化所需要的时间。
程序角度
熟练使用SQL提高查询,写出高效率的查询语句。