阅读大概需要5分钟
一个生产中的问题,建议阅读四颗星
文章目录
现象
redis-cluster某个分片内存飙升,明显比其他分片高很多,而且持续增长。并且主从的内存使用量并不一致。(个别可能导致服务无法启动)
分析可能原因
对于 redis 出现这种现象,一般都会从这个这几个方面考虑
- redis-cluster的bug (这个应该不存在)
- 客户端的hash(key)有问题,造成分配不均。(redis使用的是crc16, 不会出现这么不均的情况)
- 存在个别大的key-value: 例如一个包含了几百万数据set数据结构(这个有可能)
- 主从复制出现了问题。
- 其他原因
调查原因
- 经查询,上述1-4都不存在
- 观察info信息,有一点引起了怀疑: client_longes_output_list有些异常。
- 于是理解想到服务端和客户端交互时,分别为每个客户端设置了输入缓冲区和输出缓冲区,这部分如果很大的话也会占用Redis服务器的内存。
从上面的 client_longest_output_list
看,应该是输出缓冲区占用内存较大,也就是有大量的数据从Redis服务器向某些客户端输出。
于是,使用 client list
命令(类似于 mysql processlist)redis-cli -h host -p port client list | grep -v "omem=0"
,来查询输出缓冲区不为0的客户端连接,于是查询到祸首 monitor
,于是豁然开朗。
monitor
的模型是这样的,它会将所有在 Redis 服务器执行的命令进行输出,通常来讲 Redis 服务器的 QPS 是很高的,也就是如果执行了 monitor 命令,Redis服务器在 Monitor 这个客户端的输出缓冲区又会有大量“存货”,也就占用了大量 Redis 内存。
概念详细介绍
这是 redis 的配置介绍,对于客户端内存限制
redis 客户端在于 redis 服务端相互交互时,服务端会为每个客户端分别设置输入缓存区和输出缓存区,我们知道 redis 是基于内存的非关系型数据库,所以这两部分缓存区的数据依然是保存在内存中,如果这两部分很大的话会占用 redis 很大的内存。模型图如下图所示:
使用 info
命令查看当前redis客户端连接数及当前缓存区的使用量
那么什么是输入缓存区和输出缓存区呢?
输入缓存区:用于保存客户端发送的请求,输入缓存区的大小会根据请求数据的大小动态变化,1GB 是输入缓存区最大的容量,如果超出最大容量该客户端便会被关闭。
输出缓存区:用于保存执行请求后的返回结果,每个客户端有两个缓存区,一个是固定客户端缓存区(用于保存那些长度比较小的返回值,如一些比较短的字符串或整数值);一个是可变客户端缓存区(用于保存那些长度比较大的返回值,如长度比较大的字符串、大集合等)。
服务器采用软性限制和硬性限制两种方式限制输出缓存区的大小。
软性限制:如果软性限制所设置的大小小于输出缓冲区的大小,且输出缓冲区的大小不大于硬性限制所设置的大小,那么服务器会使用客户端状态结构的 obuf_soft_limit_reached_time
属性来记录客户端达到软性限制的起始时间。之后服务器会继续监视客户端,如果这个缓冲区的大小一直超出软性限制,并且持续时间超过服务器设定的时长,那么服务器将会关闭这个客户端。相反地,如果输出缓冲区的大小在指定时间范围之内没有超过软性限制,那么这个客户端不会被关闭,并且 obuf_soft_limit_reached_time
属性的值也会被设置为 0。
如果输出缓存区的大小大于硬性限制,那么客户端会立即关闭。
在 redis 配置文件中,可以对软性限制和硬性限制进行设置:
第一行代码表示:对于 普通
客户端来说,限制为 0,也就是不限制。因为普通客户端通常采用阻塞式的消息应答模式,通常情况下不会导致 redis 输出缓存区数据堆积膨胀。
第二行代码表示:对于 slave
客户端来说,硬性限制是 256M,软性限制是当客户端缓冲区大小持续 60秒 超过 64M,则关闭客户端连接。
第三行代码表示:对于 Pub/Sub
客户端(也就是发布/订阅模式),硬性限制是 32M,当输出缓冲区超过 32M 时,会关闭连接。软性限制是当客户端缓冲区大小持续 60秒 超过 8M,则关闭客户端连接;
紧急处理办法
进行主从切换(主从内存使用量不一致),也就是 redis-cluster 的 fail-over 操作,继续观察新的 Master 是否有异常,通过观察未出现异常。
查找到真正的原因后,也就是 monitor,关闭掉 monitor
命令的进程后,内存很快就降下来了。
预防办法
- 修改客户端源代码,禁止掉一些危险的命令(shutdown, flushall, monitor, keys *),当然还是可以通过redis-cli来完成;
- 添加command-rename配置,将一些危险的命令(flushall, monitor, keys * , flushdb)做rename;
- 对 redis 使用中遇到的问题分享、学习分享很重要。
参考博文:
https://www.iteye.com/blog/carlosfu-2254154