Hive插入多个分区时OOM故障解决记录

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hive插入多个分区时OOM故障解决记录相关的知识,希望对你有一定的参考价值。

参考技术A 一、故障情景

基于Hive的数据仓库中需要做一张累积快照表,记录了客户发生各个行为的具体日期,比如激活日期、注册日期、申请日期、创建订单日期等等。

这张表需要以激活日期作为分区时间,便于业务查询。

激活日期将近500个日期,在一次性将所有数据全量插入目标分区的时候所有reduce报OOM。

二、解决过程

首先增加reduce端的内存,set mapreduce.reduce.java.opts = -Xmx3072m;set mapreduce.reduce.memory.mb = 3072;最后将内存设置到集群最高内存,仍然报OOM。

再分析源数据的分类,发现源数据存在以前的测试数据,这样会造成有的分区日期只有一条数据,有的分区天数有几十万数据,数据存在严重的倾斜。

解决方法有两种。

第一种在HQL末尾加上distribute by和sort by,或者cluster by。如下:
insert overwrite table loan_f_milestone partition(day)
select *,
to_date(atv_tim)
from loan_f_milestone_tmp12
distribute by to_date(atv_tim)
sort by to_date(atv_tim)
--cluster by to_date(atv_tim)

distribute by按照指定的字段将数据划分到不同的输出reduce中,可以保证每个reduce处理的数据范围不重叠,每个分区内的数据是没有排序的。

sort by保证一个reduce内的数据按照指定字段排序。

cluster by除了有distribute by的功能,还有sort by的功能,所以最终的结果是每个reduce处理的数据范围不重叠,而且每个reduce内的数据按照指定字段排序,而且可以做到全局排序。

所以第二种方式是设置set hive.optimize.sort.dynamic.partition=true;会将动态分区字段全局排序。

使用Hive SQL插入动态分区的Parquet表OOM异常分析

温馨提示:要看高清无码套图,请使用手机打开并单击图片放大查看。


Fayson的github:https://github.com/fayson/cdhproject


提示:代码块部分可以左右滑动查看噢


1.异常描述



当运行“INSERT ... SELECT”语句向Parquet或者ORC格式的表中插入数据时,如果启用了动态分区,你可能会碰到以下错误,而导致作业无法正常执行。


Hive客户端:


Task with the most failures(4):
Diagnostic Messages for this Task:
Error: GC overhead limit exceeded
...
FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask
MapReduce Jobs Launched:
Stage-Stage-1: Map: 1 HDFS Read: 0 HDFS Write: 0 FAIL
Total MapReduce CPU Time Spent: 0 msec

(可左右滑动)


YARN的8088中查看具体map task报错:


2017-10-27 17:08:04,317 FATAL [main] org.apache.hadoop.mapred.YarnChild: Error running child : java.lang.OutOfMemoryError: GC overhead limit exceeded

(可左右滑动)


2.异常分析



Parquet和ORC是列式批处理文件格式。这些格式要求在写入文件之前将批次的行(batches of rows)缓存在内存中。在执行INSERT语句时,动态分区目前的实现是:至少为每个动态分区目录打开一个文件写入器(file writer)。由于这些缓冲区是按分区维护的,因此在运行时所需的内存量随着分区数量的增加而增加。所以经常会导致mappers或reducers的OOM,具体取决于打开的文件写入器(file writer)的数量。

 

通过INSERT语句插入数据到动态分区表中,也可能会超过HDFS同时打开文件数的限制。

 

如果没有join或聚合,INSERT ... SELECT语句会被转换为只有map任务的作业。mapper任务会读取输入记录然后将它们发送到目标分区目录。在这种情况下,每个mapper必须为遇到的每个动态分区创建一个新的文件写入器(file writer)。mapper在运行时所需的内存量随着它遇到的分区数量的增加而增加。


3.异常重现与解决

3.1.生成动态分区的几个参数说明



hive.exec.dynamic.partition

 

默认值:false

 

是否开启动态分区功能,默认false关闭。

 

使用动态分区时候,该参数必须设置成true;

 

 

hive.exec.dynamic.partition.mode

 

默认值:strict

 

动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区。

 

一般需要设置为nonstrict

 

 

hive.exec.max.dynamic.partitions.pernode

 

默认值:100

 

在每个执行MR的节点上,最大可以创建多少个动态分区。

 

该参数需要根据实际的数据来设定。

 

比如:源数据中包含了一年的数据,即day字段有365个值,那么该参数就需要设置成大于365,如果使用默认值100,则会报错。

 

 

hive.exec.max.dynamic.partitions

 

默认值:1000

 

在所有执行MR的节点上,最大一共可以创建多少个动态分区。

 

同上参数解释。

 

 

hive.exec.max.created.files

 

默认值:100000

 

整个MR Job中,最大可以创建多少个HDFS文件。

 

一般默认值足够了,除非你的数据量非常大,需要创建的文件数大于100000,可根据实际情况加以调整。

 

mapreduce.map.memory.mb

 

map任务的物理内存分配值,常见设置为1GB,2GB,4GB等。

 

mapreduce.map.java.opts

 

map任务的Java堆栈大小设置,一般设置为小于等于上面那个值的75%,这样可以保证map任务有足够的堆栈外内存空间。

 

mapreduce.input.fileinputformat.split.maxsize

mapreduce.input.fileinputformat.split.minsize

 

这个两个参数联合起来用,主要是为了方便控制mapreduce的map数量。比如我设置为1073741824,就是为了让每个map处理1GB的文件。



3.2.一个例子



Fayson在前两天给人调一个使用Hive SQL插入动态分区的Parquet表时,总是报错OOM,也是折腾了很久。以下我们来看看整个过程。

 

1.首先我们看看执行脚本的内容,基本其实就是使用Hive的insert语句将文本数据表插入到另外一张parquet表中,当然使用了动态分区。



2.我们看看原始数据文件,是文本文件,一共120个,每个30GB大小,总共差不多3.6TB。


使用Hive SQL插入动态分区的Parquet表OOM异常分析


3.我们看看报错


使用Hive SQL插入动态分区的Parquet表OOM异常分析

使用Hive SQL插入动态分区的Parquet表OOM异常分析


4.因为是一个只有map的mapreduce任务,当我们从YARN的8088观察这个作业时可以发现,基本没有一个map能够执行成功,全部都是失败的。报上面的错误。


使用Hive SQL插入动态分区的Parquet表OOM异常分析


5.把mapreduce.map.memory.mb从2GB增大到4GB,8GB,16GB,相应mapreduce.map.java.opts增大到3GB,6GB,12GB。依旧报错OOM。


6.后面又将mapreduce.input.fileinputformat.split.maxsize从1GB,减少为512MB,256MB,从而增大map数量,缩小单个map处理文件的大小。依旧报错OOM。


7.最后启用hive.optimize.sort.dynamic.partition,增加reduce过程,作业执行成功。


使用Hive SQL插入动态分区的Parquet表OOM异常分析

使用Hive SQL插入动态分区的Parquet表OOM异常分析


8.最后查看结果文件大约1.2TB,约为输入文件的三分之一。一共1557个分区,最大的分区文件为2GB。


4.异常总结



对于这个异常,我们建议有以下三种方式来处理:


1.启用hive.optimize.sort.dynamic.partition,将其设置为true。通过这个优化,这个只有map任务的mapreduce会引入reduce过程,这样动态分区的那个字段比如日期在传到reducer时会被排序。由于分区字段是排序的,因此每个reducer只需要保持一个文件写入器(file writer)随时处于打开状态,在收到来自特定分区的所有行后,关闭记录写入器(record writer),从而减小内存压力。这种优化方式在写parquet文件时使用的内存要相对少一些,但代价是要对分区字段进行排序。

SET hive.optimize.sort.dynamic.partition=true; 
INSERT OVERWRITE TABLE [table] SELECT ...


2.第二种方式就是增加每个mapper的内存分配,即增大mapreduce.map.memory.mb和mapreduce.map.java.opts,这样所有文件写入器(filewriter)缓冲区对应的内存会更充沛。


3.将查询分解为几个较小的查询,以减少每个查询创建的分区数量。这样可以让每个mapper打开较少的文件写入器(file writer)。

 

备注:


默认情况下,Hive为每个打开的Parquet文件缓冲区(file buffer)分配128MB。这个buffer大小由参数parquet.block.size控制。为获得最佳性能,parquet的buffer size需要与HDFS的block size保持对齐(比如相等),从而使每个parquet文件在单个HDFS的块中,以便每个I/O请求都可以读取整个数据文件,而无需通过网络传输访问后续的block。

 

-- set Parquetbuffer size to 256MB (in bytes)
set parquet.block.size=268435456;

 

参考:


https://cwiki.apache.org/confluence/display/Hive/Configuration+Properties

http://blog.cloudera.com/blog/2014/03/how-to-use-parquet-with-impala-hive-pig-mapreduce/

https://www.cloudera.com/documentation/enterprise/latest/topics/cdh_ig_parquet.html

https://issues.cloudera.org/browse/IMPALA-2521

https://issues.apache.org/jira/browse/HIVE-6455

http://blog.csdn.net/qq_26937525/article/details/54946281



提示:代码块部分可以左右滑动查看噢


为天地立心,为生民立命,为往圣继绝学,为万世开太平。

温馨提示:要看高清无码套图,请使用手机打开并单击图片放大查看。



推荐关注Hadoop实操,第一时间,分享更多Hadoop干货,欢迎转发和分享。


以上是关于Hive插入多个分区时OOM故障解决记录的主要内容,如果未能解决你的问题,请参考以下文章

使用Hive SQL插入动态分区的Parquet表OOM异常分析

Windows还原系统后分区故障解决方案

使用Hive SQL插入动态分区的Parquet表OOM异常分析

使用Hive SQL插入动态分区的Parquet表OOM异常分析

MBR扇区故障修复

MBR引导扇区故障恢复