记一次线上内存溢出

今天上班刚坐到位置上,准备开始摸鱼。呸,开始工作。产品就在钉钉上怼过来一张截图,图上显示dmp系统的几个在跑任务挂了,最后运行时间是昨天下午,状态还是运行中。

于是我放下手中还没啃完的烧麦,开始bug时间。从表象上来看,看不出是什么问题,因为这个业务流程相对来说比较长,每一个环节都有可能出现异常,也许是因为没有考虑到的异常未被捕获造成的。心里这样想着,cd到这个业务昨天的日志文件,是他是他就是他,我们的老朋友。看到这东西,莫名开始激动了起来呢,毕竟是线上为数不多可以看到的情况。

1
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded

在此之前呢还有

1
nested exception is com.mongodb.MongoException$CursorNotFound: Cursor 2114193476805984430 not found on server

于是自然的开始Google一把这个错误,说是驱动版本的问题或者响应超时。快速排出了第一种情况,那么MongoDB怎么就超时了呢。细想一下,最近开发的功能。有一处读取Hive清洗的dmp原始数据到MongoDB,这个部分是写过程但是并没有出现Hive相关的报错信息。因此最大的可能就是落在读取MongoDB这部分数据打包成压缩文件的这个点了。

果然排查之后发现,在昨天的下午这个时间点。有业务人员操作了一个400W数据的包体,并且使用了其管理的5个账号。当时为了能够在同一时间处理多个任务,以及考虑到第一期清洗数据单个包体不会超过300W的情况下,开启线程池同时执行的最大线程数设置成了8。

因此在这种情况下,几乎同一时段加载到内存当中的数据为5x400W=2000W 而该Java进程设置的最大内存为4G,所以超出了内存上限导致溢出。

问题定位到了,就好解决了。与产品和运维沟通。该业务并不要求实时性,因此把并发线程数降到3,同时扩展内存到6G。对于大数据量的包体,进行切分,分批次查询数据append进文件中再打包。同时观察线上运行情况,及时调整,做好失败重试方案。