一、引子
今天在跑一段很复杂而且涉及数据量10多年的N个表join的长SQL时,发生了OOM的异常。
由于一个map通常配置只有64MB或者128MB,则在Map阶段出现OOM的情况很少见。所以一般发生在reduce阶段。
但是今天这个异常详细的看后,会发现既不是map阶段,也不是reduce阶段,发现不是执行过程,而是driver提交job阶段就OOM了。
Hive中XMLEncoder序列化MapredWork引发OutOfMemoryError
XMLEncoder导致java.lang.OutOfMemoryError: GC overhead limit exceeded
二、概括回顾
先概括下,Hive中出现OOM的异常原因大致分为以下几种:1. Map阶段OOM。
2. Reduce阶段OOM。
3. Driver提交Job阶段OOM。
Map阶段OOM:
1. 发生OOM的几率很小,除非你程序的逻辑不正常,亦或是程序写的不高效,产生垃圾太多。Reduce阶段OOM:
1. data skew 数据倾斜
data skew是引发这个的一个原因。key分布不均匀,导致某一个reduce所处理的数据超过预期,导致jvm频繁GC。
2. value对象过多或者过大
某个reduce中的value堆积的对象过多,导致jvm频繁GC。
解决办法:
1. 增加reduce个数,set mapred.reduce.tasks=300,。
2. 在hive-site.xml中设置,或者在hive shell里设置 set mapred.child.java.opts = -Xmx512m
或者只设置reduce的最大heap为2G,并设置垃圾回收器的类型为并行标记回收器,这样可以显著减少GC停顿,但是稍微耗费CPU。
set mapred.reduce.child.java.opts=-Xmx2g -XX:+UseConcMarkSweepGC;
3. 使用map join 代替 common join. 可以set hive.auto.convert.join = true
4. 设置 hive.optimize.skewjoin = true 来解决数据倾斜问题
Driver提交job阶段OOM:
job产生的执行计划的条目太多,比如扫描的分区过多,上到4k-6k个分区的时候,并且是好几张表的分区都很多时,这时做join。
究其原因,是 因为序列化时,会将这些分区,即hdfs文件路径,封装为Path对象,这样,如果对象太多了,而且Driver启动的时候设置的heap size太小,则会导致在Driver内序列化这些MapRedWork时,生成的对象太多,导致频繁GC,则会引发如下异常:
java.lang.OutOfMemoryError: GC overhead limit exceeded
at sun.nio.cs.UTF_8.newEncoder(UTF_8.java:53)
at java.beans.XMLEncoder.createString(XMLEncoder.java:572)
三、诊断问题
如何诊断到了问题:
Use of XMLEncoder to serialize MapredWork causes OOM in hive cli
When running queries on tables with 6000 partitions, hive cli if configured with 128M runs into OOM. Heapdump showed 37MB occupied by one XMLEncoder object while the MapredWork was 500K which is highly inefficient. We should switch to using something more efficient like XStream.I ran with 128M to investigate the OOM. We have resorted to running with 1G as XmX because we keep hitting OOM with bigger tables in hive. There were other thing