Hive 动态分区插入数据总结
1、问题描述
执行以下sql导致reduce端OOM,分区数有485个按天分区,数据主要是存量数据一次性导入数仓中遇到的问题。
服务器 :8核12G内存
Map内存参数值:
mapreduce.map.memory.mb=1024
mapreduce.map.java.opts=-Xmx768m -XX:+UseConcMarkSweepGC;
Reduce内存参数值:
mapreduce.reduce.memory.mb=2048
mapreduce.reduce.java.opts=-Xmx1536m -XX:+UseConcMarkSweepGC;
Sql语句:
分区是按照天分区:
使用以下SQL语句从ods层动态分区插入到dwd层,因为从数据库存量数据一次性抽取,数据量大概3w多,总的分区数在578个,在以上配置即使加大map以及reduce的内存,还是会抛OOM(以测试过)。
所以先抽取部分分区数据做优化测试,选取100多个不到120个分区数,sql如下:
报OOM截图:
由后台日志看出,4个reduce都是内存溢出。(100多个分区,调大reduce内存是可以解决的)
导致reduce端处理的每个分区数都产生了大量的recordWriter文件写入器。FileSinkOperator: New Final Path导致内存中保存太多句柄和数据,导致OOM或者内存超被Yarn Kill。
- 异常分析
Parquet和ORC都是列式批处理文件格式,这些文件格式要求在写入文件之前将批次的行(batches of rows)缓存在内存中。在执行insert语句时,动态分区目前的实现是:至少为每个动态分区目录打开一个文件写入器(file writer)。由于这些缓冲区是按分区维护的,因此运行时所需的内存量随着分区数量的增加而增加。所以经常导致mappers或者reduces的OOM,具体取决于打开的文件写入器(file writer)的数量。
通过INSERT语句插入数据到动态分区表中,也可能会超过HDFS同时打开文件数的限制。
使用参数hive.exec.max.created.files 来控制最大创建的HDFS文件数,默认是100000。
如果没有join或者聚合操作,INSERT ....SELECT * FROM TABLE语句会被转换为只有map任务的作业。Mapper任务会读取输入记录然后将它们发送到目标分区目录。在这种情况下,每个mapper必须为遇到的每个动态分区创建一个新的文件写入器(file writer)。Mapper在运行时所需的内存量随着它遇到的分区数量的增加而增加。
- 问题解决方式
对sql进行优化
如果sql已经是最优状态
对map、reduce阶段进行调优,内存或者其他等等
如果内存够用、够大,则尝试调整以下两个参数让文件写入器(filewriter)缓冲区对应的内存会更充沛能解决问题。
mapreduce.map.memory.mb=4096
mapreduce.map.java.opts=-Xmx(4096*0.8) -XX:+UseConcMarkSweepGC;
mapreduce.reduce.memory.mb=8192
mapreduce.reduce.java.opts=-Xmx6553m -XX:+UseConcMarkSweepGC;
2.1 改变分区规则
第一种:因为每天的数据量不过千,可以以月份作为分区规则,按月分区的话,那么存量的数据动态分区插入则分区数明显很少,则执行成功。
第二种:按天分区,将存量的数据截至到今天的数据按量划分多个分区,如果数据量不大,就可以直接指定今天的日期作为分区。
比如:将存量的数据按照某个时间划分,将去年以前的数据统一规划到2020-09-18作为一个分区。
例如sql如下:
insert overwrite table lobehaviour_trade_daily partition (part)
select dp_id, buyer_nick, to_date(created), payment, 0 as item, if(to_date(modified)<'2012-01-01','2012-01-01',to_date(modified)) as part
from taobao.s_trade_hbase
where (part<'2013-10-30' or part='STABLE') and status='TRADE_FINISHED'
distribute by part;
2.2 采用distribute by分区字段
1.全表去重到中间表 时 distribute by 分区字段,这样就可以 将相同分区的数据放到同一个文件中 用时 15 分钟
2.新处理过的中间表 动态分区至原表。
这种distribute by(列名)这种分区决定了reduce数,reduce数决定了文件数,虽然可以减少文件数,但是会导致数据倾斜的产生。
distribute by如同parititon分区,map端数据按照某个字段或者rand()随机数进行hash算法分发到各个分区中,有助于缓解数据倾斜问题,将数据均匀的分布到各个reduce中。
采用distribute by rand()测试下结果:还是报OOM。
2.3 hive.optimize.sort.dynamic.partition
针对上面的解决方式还是抛出OOM,查看了官方文档,有这个参数:官方翻译:启用该值时,动态分区列将被全局排序。这样就可以为reduce中的每个分区值只打开一个文件写入器(file writer),从而极大的减少reducer的内存压力。
在hive 0.13版本中加入并默认为true,hive0.14后默认为false,如果为true 的话,只为每个分区产生一个文件写入器,可以解决动态分区插入时的OOM问题,但会严重降低reducer处理写入一个分区的速度。
需要根据综合业务考虑是否开启,一般数据量不大的情况下,保持默认值。
本问题直接开启,问题完美解决。
- 涉及到的参数调优
4.1 hive.exec.dynamic.partition
默认值:false,是否开启动态分区功能,使用动态分区时,必须开启
4.2 hive.exec.dynamic.partition.mode
默认值:strict,动态分区模式,表示必须指定一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区。
4.3 hive.exec.max.dynamic.partitions.pernode
默认值:1000,在每个执行mr的节点上,最大可以创建的动态分区。
4.4 hive.exec.max.dynamic.partitions
默认值:1000,在所有执行mr的节点上,最大一共可创建的动态分区数。
4.5 hive.exec.max.created.files
默认值:100000,整个mr job中,最大可以创建多少个HDFS文件。
4.6 mapreduce.map.memory.mb
map任务的物理内存分配值,常见设置1G\2G\4G
4.7 mapreduce.map.java.opts
Map任务的java堆栈大小设置,一般设置为小于等于mapreduce.map.memory.mb的80%,可以保证map任务有足够的堆栈外内存空间。
4.8 mapreduce.reduce.memory.mb
reduce任务的物理内存分配值,常见为map任务分配值的2倍。
4.9 mapreduce.reduce.java.opts
同map任务分配的堆栈方式,80%
5.0 mapreduce.input.fileinputformat.split.maxsize、mapreduce.input.fileinputformat.split.minsize
Maxsize默认:256M主要是为了方便控制mapreduce的map数量,降低max值,则map数量就越多,max越大,map数量越少。
5.1 hive.optimize.sort.dynamic.partition
默认值:false,动态分区时,只为reduce产生一个文件写入器,从而减少reduce端的内存压力。
参考博文:https://blog.csdn.net/lzw2016/article/details/97818080
https://www.it1352.com/859957.html
https://docs.microsoft.com/zh-cn/archive/blogs/shanyu/hadoop-yarn-memory-settings-in-hdinsight