Hive读取MongoDB BSON文件执行count(*)时触发Java堆内存溢出
这个问题我之前帮团队排查过好几次,本质是MongoDB Hadoop连接器在处理BSON文件分片时,堆内存不足导致的。从错误堆栈能看到,溢出发生在BSONSplitter.getStartingPositionForSplit方法里——这个方法需要扫描BSON文件来确定分片的起始位置,过程中会把部分BSON数据加载到JVM堆内存,如果文件过大、单条文档体积超标,或者Hive任务的堆内存配额太小,就会触发OutOfMemoryError,尤其是首次执行查询时没有缓存,全量扫描的内存压力会更明显。
下面是几个经过验证的解决方案,按优先级排序:
调大Hive Map任务的堆内存配额
这是最直接的解决办法,你可以在执行查询前临时设置参数:-- 针对MapReduce引擎 SET mapreduce.map.memory.mb=4096; SET mapreduce.map.java.opts=-Xmx3072m; -- 如果用Tez引擎,替换成下面的配置 SET tez.container.size=4096; SET tez.java.opts=-Xmx3072m;注意:
java.opts的堆内存建议设为容器内存的70%-80%,避免容器内存不足被YARN杀掉。如果需要全局生效,可以修改Hive配置文件hive-site.xml里对应参数的默认值。禁用CombineHiveRecordReader合并分片
错误堆栈里提到的CombineHiveRecordReader会合并小分片,反而可能让单个Reader处理更大的数据块,加剧内存压力。你可以通过设置禁用分片合并:SET hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat;这样每个分片更小,单个Map任务需要加载的数据量也会减少,降低堆内存溢出的概率。
拆分超大BSON文件
如果你的BSON文件本身体积特别大(比如几十GB以上),建议先把它拆分成多个小文件:- 可以用MongoDB的
bsondump工具把BSON转成JSON,再按行数拆分后转回BSON; - 或者用
mongodump导出时指定查询条件,把数据分成多个小BSON文件。
小文件能让Hadoop的分片更细,每个任务处理的数据量可控。
- 可以用MongoDB的
排查并处理超大BSON文档
有时候问题不是文件大,而是里面有个别超大文档(比如包含大二进制数据、超长文本)。你可以用bsondump工具解析文件,找出这些大文档:bsondump your_file.bson | grep -E '"_id":.*' | awk '{print length($0), $0}' | sort -nr找到超大文档后,可以单独提取处理,或者在导入前就拆分这类文档。
另外要注意:首次执行查询时没有缓存,内存压力比后续执行大,调参后建议多测试几次;如果是集群环境,要确保YARN节点有足够的内存资源分配给Hive任务,避免出现物理内存不足的情况。
内容的提问来源于stack exchange,提问作者supersujj

