一个 redis 异常访问引发 oom 的案例分析

标签: oom redis blog | 发表时间:2014-12-06 08:00 | 作者:
出处:http://mindwind.me/

「推断的前提是以事实为依据。」

这两天碰到一个线上系统的偶尔出现突然堆内存暴涨,这倒不是个什么疑难杂症, 只是过程中有些思路觉得可以借鉴参考,故总结下并写下来。

现象

内存情况可以看看下面这张监控图。

一天偶尔出现几次,持续时间一般几分钟不等。 当这种情况出现时,我们检查错误日志,发现有下面两几种 OOM 错误。

  • java.lang.OutOfMemoryError: GC overhead limit exceeded
  • java.lang.OutOfMemoryError: Java heap space

与之相伴的还有个错误日志,是访问 redis 抛出的异常

  • JedisConnectionException: java.net.SocketException: Broken pipe

我们一共观察到的现象大概就是这么多了,观察了两天感觉现象发生没什么规律性,持续时间也不长,一会儿应用 又会自动就恢复正常了。

诊断

通过上面的现象,负责系统开发和维护的童鞋认为可能是网络不稳定, java.net.SocketException: Broken pipe 这个异常看起来确实是连接 redis 的长连接中断了, 而出现这个问题的应用,正好是我们新部署在一个新的 IDC,它需要访问在老 IDC 部署的 redis, 而在老 IDC 部署的应用则没出现过此类现象。

虽然两个 IDC 之间通过高宽带光纤连接作成了局域网,但依然比同一 IDC 内相比要慢上一些,再加上这个伴生 的应用抛出的网络异常,让人容易判断是网络环境的稳定性可能有区别导致应用行为的差别。 只是连接 redis 的长连接中断和应用抛出 OOM 有什么关联?我咋一想没觉得有必然联系。 而且负责网络监控的同事也确定两个 IDC 之间在发生应用异常时网络很稳定,完全没有丢包现象,带宽也足够。 因此将其原因推断于网络不稳定,就显得让人不太能理解,难以信服。

而且在 OOM 中能自己恢复的应用就不是内存泄露,应该属于内存溢出。 大概可能就是应用申请的内存短时间内超出了 JVM 堆的容量,导致抛出 OOM,从上面抛出的两种类型的 OOM 看确实像这种情况,特别是提示 GC overhead limit exceeded 这个说明就更指向了代码可能有问题。 只是如何找到哪段代码有问题,这个只好先通过在 OOM 时 dump 内存来分析了,在应用启动时加入下面启动参数 来捕捉 OOM 现场。

  • -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=mem.dump

弄到了内存 dump 文件后,用 jhat 或 MAT 分析,顺利找到了某个线程在当时申请了 1.6G 内存,再顺着 线程栈找到了调用方法,一看源码立刻明白了,代码所在方法提供了对外的接口服务,方法参数来自外部输入,没有 对输入参数作安全性判断,而是直接根据输入参数确定边界创建了一个超级大的数组(2000多万个整数),导致立刻触发 了 OOM 并持续 FullGC 一段时间后被直接回收了,所以内存曲线才会像上图中那样。

再想想

现象中还有个连接 redis 的网络异常,这又是怎么回事? 再回到代码去看,原来那个拼出来的 2000 多万个整数元素数组,是作为访问 redis 的命令参数( hmget)。 到这里,瞬间明白了是吧,这么长的参数做过服务端网络编程的都明白,协议解析时超过一个合理长度估计就会被拒绝 被认为是恶意的客户端,而导致服务端拒绝该客户端,拒绝的行为一般都是关闭连接。

再去扒了下 redis 的文档,确实找到了这样的说明:

Query buffer hard limit Every client is also subject to a query buffer limit. This is a non-configurable hard limit that will close the connection when the client query buffer (that is the buffer we use to accumulate commands from the client) reaches 1 GB, and is actually only an extreme limit to avoid a server crash in case of client or server software bugs.

上面就是说 redis 最大能接受的命令长度是写死在代码里地,就是 1 GB,超过这个自然被拒绝了。 更多关于细节参看 redis 官方文档

总结

我觉得从这个案例中收获了两点感悟:

  1. 现象并不那么可靠,不能头痛医头脚痛医脚。
  2. 先从怀疑自己的代码开始。

第一点,应该是个常识了,医生诊断的例子充分说明了这点。 第二点,为什么要先从怀疑自己代码开始呢,简单来说就是应用的业务代码通常是测试和验证最不充分的代码。 业务应用依赖的环境不论是硬件(主机、网络、交换机)的还是软件的(操作系统、JVM、三方库)这些通常都比业务代码 经过更多地测试和广泛地应用验证,所以要先从怀疑自己开始,除非有非常明确地证据指向其他方面, 个人经验大部分时候这都是找到问题的最短路径。

相关 [redis 异常 访问] 推荐:

一个 redis 异常访问引发 oom 的案例分析

- - mindwind
「推断的前提是以事实为依据. 这两天碰到一个线上系统的偶尔出现突然堆内存暴涨,这倒不是个什么疑难杂症, 只是过程中有些思路觉得可以借鉴参考,故总结下并写下来. 内存情况可以看看下面这张监控图. 一天偶尔出现几次,持续时间一般几分钟不等. 当这种情况出现时,我们检查错误日志,发现有下面两几种 OOM 错误.

Redis实现lock互斥访问资源

- - 数据库 - ITeye博客
Redis是当前很流行的一种开源键值数据库. 目前睿思的后台架构在数据库层采用了Redis和MySQL组合的形式,其中Redis主要用来存储状态信息(比如当前种子的peer)和读写频繁的数据. Redis完全运行在内存之上,无lock设计,速度非常快. 通过实测,在睿思服务器上读写速度达到3万次/s.

Google和SugarSync访问异常

- Freeman - 月光博客
  今天下午,Https的Google出现了短时间的访问异常,国内各地网友发现其访问出现不稳定状态,有时可以访问,有时无法访问.   这对中国用户最直接的影响是,很多用户无法访问Https版Google Reader,还有一些用户无法登录Gmail邮箱.   今天的另一个坏消息是,知名美国云存储服务SugarSync网站的域名被关键字屏蔽,目前已经无法从中国访问,虽然SugarSync的上传速度比起Dropbox还有差距,但至少美国的服务用起来放心一些,早先云存储服务Dropbox被屏蔽时不少网友转到了SugarSync这个服务,不过值得庆幸的是,该服务的电脑客户端和iPhone手机端都可以正常使用,上传和下载文件都没有问题.

Redis持久化文件异常原因以及修复方法

- - 丕子
上线了Redis的模块,昨天Redis Server重启了,但是Redis Instance起不来了,同学联系我,我也不知道具体情况,就猜了几个原因:1、持久化文件可能太大,没load完,所以短时间内启动不了.  2、主从配置文件被sentinel修改乱了. 最后查了下原因,是由于机房突然断电导致redis aof持久化文件写入异常,这个问题详见: link.

mysql突然出现大量慢sql,随后redis访问超时

- - Linux - 操作系统 - ITeye博客
在亚马逊云买了多台的虚拟主机,一年多没有由于系统的原因出过故障. 早上接到报警,从业务故障上来看,应该是数据库没有响应了. SSH连数据库服务器,发现连不上. 重启数据库服务器,一直起不来. 最后用上周的数据库服务器的系统备份snapshot(我们的数据盘和系统盘是分开的)新建一个Volume,替换掉故障系统盘,重新启动服务器,才顺利进入系统.

高性能kv存储之Redis、Redis Cluster、Pika:如何应对4000亿的日访问量?

- - 运维派
随着360公司业务发展,业务使用kv存储的需求越来越大. 为了应对kv存储需求爆发式的增长和多使用场景的需求,360web平台部致力于打造一个全方位,适用于多场景需求的kv解决方案. 目前,我们线上大规模使用的kv存储有Redis,Redis cluster以及Pika. 为什么说是爆发式的需求增长呢.

Redis BGSAVE因为内存不足 fork 失败导致目标 Redis 无法访问的问题 - piperck - 博客园

- -
中秋的时候正在外面愉快的在外卖喝着咖啡玩电脑. 突发 redis 报警从 sentry 应用端曝出的错误. 于是又开始愉快的处理问题了,看上去像是执行 rdb 快照持久化的时候出现的问题,上到 redis 机器查看日志定位详细问题. 可以很明显的发现应该是尝试 fork 的时候内存不够,并没有被 linux 内核放行.

从解决Redis访问超时的问题谈起——故事比结果要精彩

- - the5fire的技术博客
这周终于解决了Redis访问经常超时的问题,终于可以踏实睡觉了. 从上周就开始纠结在这个问题上,可以用寝食难安来形容,感觉这个问题就像个定时炸弹一样,虽然根据手搜的访问量,极少的Timeout Error对用户基本不会造成影响,但是这种问题如果不及时遏制导致Redis整体被拖慢造成的危害是相当严重滴.

Redis 负载监控——redis-monitor

- - ITeye资讯频道
redis-monitor是一个Web可视化的 redis 监控程序. 使用 Flask 来开发的,代码结构非常简单,适合移植到公司内网使用. redis 服务器信息,包括 redis 版本、上线时间、 os 系统信息等等. 实时的消息处理信息,例如处理 command 数量、连接总数量等. 内存占用、 cpu 消耗实时动态图表.

Redis 起步

- - 博客园_首页
Rdis和JQuery一样是纯粹为应用而产生的,这里记录的是在CentOS 5.7上学习入门文章:. Redis是一个key-value存储系统. 和Memcached类似,但是解决了断电后数据完全丢失的情况,而且她支持更多无化的value类型,除了和string外,还支持lists(链表)、sets(集合)和zsets(有序集合)几种数据类型.