记一次生产环境因OOM造成的宕机事故
事故过程
某天下午,客户突然反映生产环境系统无法使用,页面、小程序等接口均无响应,发生了宕机,紧急重启之后恢复正常。
原因排查
华为云系统监控显示,宕机之前数据库连接数飙升,redis连接数飙升。查看该时间段日志,发现大量sql连接超时无法执行。逐步缩短时间间隔寻找慢sql的起始发生时间点,最后发现有一条sql打印后在in语句中有上万条数据,执行过慢导致数据库连接数飙升,且该查询数据量过大,写入redis和内存,导致了redis连接数飙升,堆内存打满发生OOM。
找到问题发生位置后,检查代码,该sql的in中的数据是由另一条sql查到的,执行后发现生产环境有一百多万条,难怪速度变慢。。
具体分析
为什么会查出那么多数据?检查后发现该表有一个冗余字段,在业务上改用了另一个字段,所以几乎没有写数据,但是查询中or关联了该字段,所以该字段为空的数据都查了出来,几乎是全量的。但是查询值为空在代码中应该是检查过的,寻找日志关键字后,确实找到了日志中为空的查询值。再看代码,原来使用了spring的StringUtils.isEmpty判断查询值,这样带空格的字符串就无法检查出来,而mysql中空字符串和带空格的空串是等价的,修改为apache的StringUtils.isBlank,解决问题
经验总结
排查过程:慢查询 -> cpu过高 -> 影响其他查询 -> 连接释放不及时 -> 连接数飙升 -> 数据量大导致内存飙升 -> OOM -> 宕机 -> 日志检查最早记录的慢sql -> 回到代码分析
经验心得:1.对于查询条件一定要确认数据是否可能为空,冗余经常为空的字段在条件中的存在可能会导致查询结果集过大,尽量不要留下来
2.用apache的StringUtils.isBlank判断空串,mysql的“”和“ ”查询是等价的
遗留问题:二维码扫码为什么会出现只含空格的字符串?