##线上一次故障导致客户方的应用无法使用
现象描述
业务切换到云redis后,出现业务不可用情况。通过监控看到 千兆带宽流量打满 ,内存占用情况:
查看 info memory 信息,
used_memory_human:198.23M,
used_memory_overhead:10443988934 ,也就说overhead占用超过10g左右,监控信息均正常,开始怀疑 lua,但是
used_memory_lua_human:37.00K
那么分析源码getMemoryOverheadData函数
看到 overhead内存占用主要是
初始化之后的内存(这部分很少都是一些redis出事数据结构占用)+backlog(历史增量日志一般也就几Mb)+主从复制连接读写缓存+client 读写缓存+AOF 写缓冲和 rewrite写缓存+ 数据部分的 (hash entry,object )占用。
那么查看 client list 可以看到如图,omem,占用大量内存但是不释放。参数调整到了 256mb 30秒,所以outputbuffer没有及时超过阈值释放。
client-output-buffer-limit normal 513741824 256870912 30
所以这个时候大概定位内存占用高的直接指标信息了。
#分析客户方无法使用的原因
我们在分析 client的连接,看到客户方已经迁走业务,到自建redis,但是 redis的tcp队列依然有发送队列挤压数据问题,并且看到 这些client的 omem几乎没有变化,
这是为什么?恶意使用?经过和客户方的沟通查看到 客户方的 采用jedis代码调用,但是排查代码发现 采用 java的 timeout 1秒钟作为基础,那么就释放掉jedis的连接。排查jedis代码发现,从jedis连接池中拿出来的 连接 close调用 会直接归还连接池中。
public void close() { //如果使用了连接池,检查客户端状态,归还到池中 if (dataSource != null) { if (client.isBroken()) { this.dataSource.returnBrokenResource(this); } else { this.dataSource.returnResource(this); } } else { //未使用连接池,直接关闭 client.close(); } }
完整还原:
经过分析大概是这样,客户端通过jedis发送大量的请求导致带宽打满,由于带宽打满导致 redis连接的缓冲区无法及时发送到 jedis客户端,导致业务代码超时,从而释放jedis连接,从而导致归还连接池后,再次从连接池中拿数据会导致连接中结果读取错误无法解析,从而导致业务逻辑全部都除了问题。
优化方案:jedis 超时判断这需要把jedis 直接client.close(),就不能调用连接池的close方法。还有一个就是 将大批量的带宽占用调整成集群,分摊带宽压力从而减少这类异常的发生。