G!内存溢出了

G!内存溢出了

Administrator 23 2024-10-02

一、背景

  在实际工作中会遇到不少性能问题,比如内存泄露、内存溢出、连接数达到上限、cpu使用率过高、慢响应等场景问题。最近印象比较深刻的是一个小迭代中遇到的内存溢出问题。

  仅仅新增了一个数据采集服务,本来大家都觉得挺简单的,并没有做过性能测试,结果上线不到二十分钟,告警平台就报了OOM。第一时间回滚了版本,骂一骂用户之后,马上跑到测试环境复现问题。

image.png

二、场景复现

  通过模拟线上并发数,加压到500并发,服务器的内存使用率呈现递增情况,出现内存泄漏,由此推断最终可能导致内存溢出,复现了线上的问题。

三、问题定位分析

复现问题后,我们采用了以下步骤进行定位:

  1. 打印数据采集服务的运行日志,发现数据库读写时间从一开始0.3秒,随着压测持续时间增加,增加到了1.7秒,影响到了整个事务的处理响应时间。

  2. 定位数据读写。排查了sql语句,只是简单的单表写入情况,并没有复杂的联表查询,暂时排除sql问题。

  3. 查看数据库连接池。通过数据库监控平台,我们发现了连接池达到了上限。当时的连接池上限只有50个,导致了后续的新增连接数阻塞等待。

  • 问题分析: 数据库连接数阻塞等待会导致被测服务事务的阻塞。当有新的接口请求被测服务时,之前的许多请求会由于数据读写的过程没有处理完成还在等待,导致事务的资源一直积压,没法及时释放,不断占用内存空间,出现内存泄漏,最后导致内存溢出问题。

四、解决方案

  • 方案一:提高数据库连接池上限。 但由于当时配置的连接池上限是最优情况,且若出现更高的并发,还是会导致同样的问题,治标不治本。所以该方案并不可靠,未被采取。

  • 方案二:增加Redis和定时任务。 用户触发数据上报之后。将数据先写入Redis,可以提高数据读写速率,同时可以减轻db的压力。Redis的增量数据会定期淘汰。并实现多一个定时任务,每1分钟去查Redis的数据批量写入db。用这种方式,每次的批量写入实际上也只有一个连接数。

image.png

采取了方案二后,我们重新测试进行版本发布上线,内存溢出问题没有出现,得以解决。

五、复盘总结

  问题解决后,我们也第一时间进行了完整的事故复盘。从我们测试的角度来说,根本原因是因为我们这边没有做性能测试,没有提前规避问题,或者说没有评估好这个需求是否需要做性能测试。所以后来的评审时,我们增加多了一个环节,是否需要进行性能测试这样一个评审环节,从是否高频调用接口、是否营销活动通用场景等角度来去评估。