【极客日常】分享下OOM问题排查解决的实战经验

在AI助手时代,OOM的问题排查方法论已经非常烂大街,但在实际工作中,即便我们通过很多方式查到了OOM问题,但修复还是要我们自己手动修。OOM会导致服务不可用、系统不稳定等问题,会直接影响用户体验,所以从稳定性问题解决优先级来看,是属于比较高的。

因此,今天笔者就简单从自身经验出发,聊一下OOM问题的排查和解决经验。从事情上来看,轻排查,重解决。排查的手段很多,但怎么解决,就需要我们自己根据实际情况来决策。

首先聊一下问题排查。排查用的工具比如jvm可以用arthas,排golang服务可以用pprof,或者其他开源跟企业内部自建的工具都可以,主要需要有heap-dump的能力,如果没有这个能力的话,次之只能通过日志/metrics做OOM潜在风险链路判断。

工具准备好了之后,接下来就是确定OOM的成因。服务会OOM,直接原因就是单点服务的内存容量没有办法承载业务内存泄漏的需要,第一点就需要排查业务逻辑里面,有什么节点会在一段时间内,申请大量内存缓存业务数据。比方说,某个时间点上,我开了100个线程,load了100个任务的持久化日志记录,做分析修改并落库,那么这里面数据的序列化/反序列化(业务json跟db的orm都要考虑)、日志分析时期数据在内存的驻留、分析过程中间数据的产出,都有可能导致内存爆炸。所以我们可以对这类多开操作做日志打点,判断是否可能存在风险。如果是因为死锁跟或者WaitGroup不done导致的问题,常见症状一是OOM会相对频繁,二是trace日志或者整个任务进度卡在某个点不进行下去,三是线程永不释放数量恒定增长,这些问题都是可以通过对应指标去判断的。

如果OOM相对来讲突发的,那可能问题在于具体某个任务或某个请求对应的数据量比较大,根因更像是某类代码bug,而不是代码实现未做充分保护措施引起。对于这类问题,我们可以根据heap-dump内容去反推具体哪个任务或哪类请求出问题,然后结合代码去排查为什么会产生那么多的数据。比方说,如果某个任务所有子执行项的日志打印内容容易导致OOM,那么我们需要排查任务子执行项的数量是不是异常多,如果多的话,就看代码里面是不是有什么场景遗漏导致任务子执行项无限增长。同时,一般代码bug都是通过变更引起,所以排查代码bug也可以关注OOM是什么时间开始,这个时间附近有没有某些关键逻辑的代码变更,这样就可以快速定位到问题。如果不是这种case,也可以考虑是否是上游流量突增引起某条业务链路内存占用过大,这个时候我们就需要排查HTTP、RPC、Consumer和缓存的流量和消费日志,判断可能的原因。

然后聊一下OOM的解决。通用无脑的解决方法当然是扩容,但这个操作一来是缓和线上问题做临时止损,二来有一个前置认知是业务逻辑的内存预期用量符合预期,否则OOM的问题没有办法根本解决。要根本解决OOM问题,先看如果是突发异常的话,比如某个任务子项数量有异常,那我们可以通过终止任务、限制子项查询以及任务子项的修数去做止损,然后修复对应的代码bug当下解决问题保证服务可用,然后长期去做一些对应异常的监控以及更加严格的代码CR机制来保证OOM问题能够提前发现。如果是上游流量突增导致的OOM问题,那我们首先需要上游对齐异常流量是否符合业务预期,然后再决定是优先做些通用的限流熔断措施,还是在代码里做些工作迎合业务预期。如果是死锁导致的问题,一般修完代码跟服务实例之后都应该会自动修复,继续推进相关任务进度。

如果是长期但不是很频繁的内存泄漏问题,可以在扩容基础上去优化一次性多开或者长期内存驻留的逻辑,比如可以通过函数作用域的特性让中间数据及时被gc掉,golang的话单点服务尽可能通过指针标识数据,相同但耗内存的db查询用singleflight封装(注意死锁),这些都是节省内存的办法。

版权声明
本文为博客HiKariのTechLab原创文章,转载请标明出处,谢谢~~~