解决动态分区textfile文件到ORC文件OOM问题
1.问题描述
在搭建数据仓库的过程中,在搬历史数据的过程中,先将Orace中的数据sqoop到textFile格式的HIVE表中,然后运行"Insert...Select"语句向ORC格式的表中插入数据,报错Over usage of virtual memory。
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.异常重现与解决
hive.exec.dynamic.partition
默认值:false
是否开启动态分区功能,默认false关闭。
使用动态分区的时候,该参数必须设置成true;
hive.exec.dynamic.partition.mode
默认值:strict
动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区。
一般需要设置为nonstrict
hive.exec.max.dynamic.partitions.pernode
默认值:100
在每个执行MR的节点上,最大可以创建多少个动态分区。
该参数需要根据实际的数据来设定。一般选取比实际分区数大的值作为该参数的值。
hive.exec.max.dynamic.partitions
默认值:1000
在所有执行MR的节点上,最大一共可以创建多少个动态分区。
同上参数解释。
3.2关键参数
使用上述参数后就出现了Over usage of virtual memory的问题。
随后就看到了老哥的文章Hadoop实战
发现了hive.optimize.sort.dynamic.partition参数。
hive.optimize.sort.dynamic.partition
默认值:false
启用hive.optimize.sort.dynamic.partition,将其设置为true。通过这个优化,这个只有map任务的mapreduce会引入reduce过程,这样动态分区的那个字段比如日期在传到reducer时会被排序。由于分区字段是排序的,因此每个reducer只需要保持一个文件写入器(file writer)随时处于打开状态,在收到来自特定分区的所有行后,关闭记录写入器(record writer),从而减小内存压力。这种优化方式在写parquet文件时使用的内存要相对少一些,但代价是要对分区字段进行排序。
启用了该参数后,大部分表已经可以动态分区插入了。但还是有部分大表报Over usage of virtual memory,这时候就要调内存了。
mapreduce.map.memory.mb
mapreduce.reduce.memory.mb
map、reduce任务的物理内存分配值,常见设置为1GB,2GB,4GB等
CDH中默认为1GB,此处临时设置成2GB
mapreduce.map.java.opts
mapreduce.reduce.java.opts
map、reduce任务的Java堆栈大小设置,一般设置为小于等于上面那个值的75%,这样可以保证map、reduce任务有足够的堆栈外内存空间。此处均设置成-Xmx、-Xms为1500m
通过上述配置基本可以解决OOM的问题,如果还是不行,只能将查询分解为几个较小的查询,以减少每个查询创建的分区数量。这样可以让每个mapper打开较少的文件写入器(file writer),再不济就写个脚本一天一天插入(虽然有点low,但每天数据实在太大就让它晚上自己跑吧)
内容来源 微信公众号 Hadoop实操
其实大部分都是抄的老哥的,我只是想感受下Markdown,嘿嘿
网友评论