Hive 插入动态分区表时遇到的一些坑

Hive 插入动态分区的问题

insert into table xxx partition(xxxx)
select ...

使用动态分区时首先不要忘记的一些配置:

  • 是否开启动态分区 hive.exec.dynamic.partition
  • 动态分区是否使用严格模式 hive.exec.dynamic.partition.mode
  • MR总共可创建最大分区数 hive.exec.max.dynamic.partition.partitions (默认1000)以及当前节点可创建的最大分区数 hive.exec.max.dynamic.partition.partitions.pernode (默认100)

动态分区产生大量小文件

我这里分区表的存储格式是Parquet,insert select方式动态插入动态分区中,按天分区数据量非常大,一天几十G的文件
因为数据量大,而且分区数多,就会起大量map,从而产生大量小文件(map数x分区数)。小文件过多不仅影响查询,NameNode中的元数据信息管理也会受很大影响

  • 参考:https://blog.csdn.net/mhtian2015/article/details/79898169
insert into table xxx partition(pcol)
select ...
from ...
distribute by(pcol)

这样做分区数决定了reduce数,reduce数也决定了文件数,虽然可以减少文件数,但会导致数据倾斜的产生

insert into table xxx partition(pcol)
select ...
from ...
distribute by rand()

通过rand()结果hash编码后对reducer数取余,可以把数据均分到reducer上去。数据量很大的话能保证每个reduce上都有和pcol分区数相等的文件数。所以文件数可以由reducer个数限制,hive.exec.reducers.max

顺便提一句,distribute by rand()也可以用来随机抽样,因为随机分发到reducers上去了,也就等同于随机抽取了

我尝试这样做了后,又出现了OOM错误,google了一番找到了下面这个配置参数

  • hive.optimize.sort.dynamic.partition

官网解释是:When enabled, dynamic partitioning column will be globally sorted. This way we can keep only one record writer open for each partition value in the reducer thereby reducing the memory pressure on reducers.

Hive 0.13加入并默认true,Hive 0.14后默认为false,如果为true的话,这个参数可以使得每个分区只产生一个文件,可以解决动态分区时的OOM问题,但会严重降低reduce处理并写入一个分区的速度
这里设置为true后成功了。但这个参数的后果就是慢,这篇文章有谈及要综合业务考虑是否开启:https://hdinsight.github.io/hive/hive-slow-reducer.html

动态分区插入时OOM问题的产生

还是上面hive.optimize.sort.dynamic.partition这个参数。产生OOM问题的原因是同时打开了太多写分区的record writer同时写入文件,开启该参数的话,分区列会全局排序,使得reduce端每个分区只有一个文件写入,降低reduce的内存压力

具体OOM的原因我在这里看到了:https://cloud.tencent.com/developer/article/1079007

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

https://community.hortonworks.com/content/supportkb/171090/hive-query-with-dynamic-partition-takes-long-time.html
https://community.hortonworks.com/articles/89522/hive-insert-to-dynamic-partition-query-generating.html
要是还报OOM问题,进一步可以考虑调整map、reduce的内存大 mapreduce.map.memory.mbmapreduce.reduce.memory.mb 参数

Hive 控制文件数过多的一些参数

这个是搜索上面遇到的问题时遇见的,觉得有价值,也谈及了上面提到的distribute by,所以再次记录下

参考:https://www.jianshu.com/p/e76ddc4b19c5

说的是hive.exec.max.created.files 参数,默认是10000,超过这个数会报错exceeds 100000.Killing the job

设置 mapper输入文件合并的参数

set mapred.max.split.size=256000000;  #每个Map最大输入大小
set mapred.min.split.size.per.node=100000000; #一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
set mapred.min.split.size.per.rack=100000000; #一个交换机下split的至少的大小(这个值决定了该机架下的文件是否需要合并)
set hive.input.format=org.apache.Hadoop.hive.ql.io.CombineHiveInputFormat;  # 执行Map前进行小文件合并

在开启了org.apache.hadoop.hive.ql.io.CombineHiveInputFormat后,一个data node节点上多个小文件会进行合并,合并文件数由mapred.max.split.size限制的大小决定。
mapred.min.split.size.per.node决定了多个DataNode上的文件是否需要合并
mapred.min.split.size.per.rack决定了多个交换机上的文件是否需要合并

设置 map输出和reduce输出进行合并的参数

hive.merge.mapfiles= true    #设置 map输出和reduce输出进行合并的相关参数
hive.merge.mapredfiles= true 设置reduce端输出进行合并,默认为false
hive.merge.size.per.task= 256 *1000 * 1000  设置合并文件的大小
hive.merge.smallfiles.avgsize=16000000   输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge

顺便记录下其他或许会用到的Hive调优参数:

  • https://blog.csdn.net/high2011/article/details/78999420
  • https://blog.csdn.net/renzhixin1314/article/details/70496325

更多大数据相关Tips可以关注:https://github.com/josonle/Coding-Now 和 https://github.com/josonle/BigData-Learning

你可能感兴趣的:(Hadoop及Spark学习)