环境: CentOS 6.5 Redis 2.8
问题描述: 最近几天有一个使用了俩年的redis实例的内存使用情况的增长速率很是诡异,突然从1G增长到了4个多G,一开始认为是因为新项目上线有在使用,但是询问了开发发现新上线的项目并没有使用这个redis实例.并且发现一个比较诡异的情况就是在这个redis上面只有61个hash类型的key,并且使用redis-cli -p 6379 --bigkeys 进行大key分析,分析出来的情况如下:
# Scanning the entire keyspace to find biggest keys as well as # average sizes per key type. You can use -i 0.1 to sleep 0.1 sec # per 100 SCAN commands (not usually needed). [00.00%] Biggest hash found so far ‘rc:value:0116:16841639‘ with 6 fields -------- summary ------- Sampled 61 keys in the keyspace! Total key length in bytes is 1366 (avg len 22.39) Biggest hash found ‘rc:value:0116:16841639‘ has 6 fields 0 strings with 0 bytes (00.00% of keys, avg size 0.00) 0 lists with 0 items (00.00% of keys, avg size 0.00) 0 sets with 0 members (00.00% of keys, avg size 0.00) 61 hashs with 342 fields (100.00% of keys, avg size 5.61) 0 zsets with 0 members (00.00% of keys, avg size 0.00)
可以比较明显得看得到,所有的key加起来的值非常的小,几乎可以排除存在大key。
然后查看有key和value的内存的占用情况:使用的工具是 redis-rdb-tools分析redis的内存分析:
redis-rdb-tools的安装使用:https://help.aliyun.com/knowledge_detail/50037.html
[[email protected]~]# rdb -c memory 6379dump.rdb database,type,key,size_in_bytes,encoding,num_elements,len_largest_element 0,hash,rc:value:0116:16841639,142,ziplist,6,10 0,hash,rc:value:0118:30680413,142,ziplist,6,10 0,string,limit_34864207,88,string,8,8 0,hash,rc:hammer:color:0121:30680413,109,ziplist,3,8 0,string,limit_28962590,88,string,8,8 0,hash,rc:value:0116:30755821,142,ziplist,6,10 0,hash,rc:draw:14073376,115,ziplist,4,8 0,hash,rc:value:0119:30680413,142,ziplist,6,10 0,string,limit_34994213,88,string,8,8 0,string,limit_35067785,88,string,8,8 0,string,limit_33964751,88,string,8,8 0,hash,rc:value:0117:20638549,142,ziplist,6,10 0,hash,rc:value:0117:32835296,142,ziplist,6,10 0,hash,rc:value:0117:33047903,142,ziplist,6,10 0,hash,rc:value:0118:31405910,142,ziplist,6,10 0,string,limit_31912444,88,string,8,8 0,string,limit_34745689,88,string,8,8 0,hash,rc:value:0117:30755821,142,ziplist,6,10
由于篇幅过长,所以部分分析结果没有打印出来,但是根据打印出来的size_in_bytes的计算统计发现总共才3w多bytes.所以基本上排除了key-value过大占用了这么多的内存。
Google了一番之后,对于这种情况的解释大部分的解释都是基于redis的空间碎片,因为redis的内存分配都是基于page/block为最小单位分配,在删除掉del的时候由于这个page上面还有其他的key,所以这个page是不能释放的,只有这个page上面的所有key删除掉才能释放;但是针对于我这个情况来说,那怕一个key使用一个page,一个page我设置为16K或者更大,这些加起来也是61*16K 这么多,离4G还是有非常大的距离,所以这个情况可以排除掉了.
在毫无头绪的情况下,无意中发现这个redis的链接有点不对劲,redis-cli -p 6379 info stats观察一端时间发现OPS基本是1000左右,但是在连接数这块却有3000的样子,并且链接数也是在这几天由1000增长到3000,然后我猜想有没有可能和链接数有关系呢?
redis-cli -p 6379 client list
使用上诉命令查看redis内部的链接情况:
id=8546905 addr=x.x.x.x:14061 fd=1094 name= age=5014815 idle=3639147 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8546981 addr=x.x.x.x:14858 fd=1596 name= age=5014439 idle=3640850 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8546957 addr=x.x.x.x:14768 fd=1590 name= age=5014540 idle=3639017 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8546960 addr=x.x.x.x:14798 fd=1593 name= age=5014535 idle=3641126 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8547541 addr=x.x.x.x:22018 fd=1599 name= age=5011777 idle=3641812 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8546930 addr=x.x.x.x:14457 fd=1425 name= age=5014675 idle=3640389 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8547242 addr=x.x.x.x:18366 fd=1598 name= age=5013182 idle=3640371 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8546959 addr=x.x.x.x:14789 fd=1591 name= age=5014536 idle=3639087 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8546908 addr=x.x.x.x:14138 fd=1116 name= age=5014786 idle=3639026 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8546980 addr=x.x.x.x:14842 fd=1595 name= age=5014443 idle=3640372 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8546979 addr=x.x.x.x:14806 fd=1594 name= age=5014449 idle=3638217 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush id=8546924 addr=x.x.x.x:14143 fd=1161 name= age=5014709 idle=3638929 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=lpush
上诉是问题解决之后的部分输出.几个重要的参数解释:
-age: 链接的存活时长 -idl: 链接的空闲时长 -qbuf: 客户端的缓冲区的容量 -qbuf-free: 缓冲区的空闲容量
一开始我并没有对这些参数很是重视,只是针对idl的时间很长的链接做了kill操作:
client-cli -p 6379 client kill ip:port
在kill掉很多idl很长的链接的时候,在去看used_memory的值确实降了下来,但是还是有3G的样子,那么可以比较确认这个used_memory的值这么大是和链接有关系的.然后翻阅了资料获取得到:
(2)输入缓冲区:qbuf、qbuf-free
Redis为每个客户端分配了输入缓冲区,它的作用是将客户端发送的命令临时保存,同时Redis从会输入缓冲区拉取命令并执行,输入缓冲区为客户端发送命令到Redis执行命令提供了缓冲功能,如图4-5所示。
client list中qbuf和qbuf-free分别代表这个缓冲区的总容量和剩余容量,Redis没有提供相应的配置来规定每个缓冲区的大小,输入缓冲区会根据输入内容大小的不同动态调整,只是要求每个客户端缓冲区的大小不能超过1G,超过后客户端将被关闭
链接:http://www.jianshu.com/p/70f3b68a7fd7
然后在查看链接,发现有些链接的qbuf的值将近50M,但是qbuf-free却是0,并且这样子的链接并不在少数,有几百个的样子.然后在查阅这些链接的idl已经很长了,将这些链接kill之后,used_memory的值厘米降下来了,只有几十M的样子了。
used_memory统计的是redis实例分配的内存,其中还包含redis的内存管理和一些内存的stats信息,并不仅仅是data信息.
这些链接这么多的原因是因为在几周之前client有做了迁移,但是旧的服务并没有立即关掉只是在这几天才把服务器关掉的,但是由于client没有主动关闭链接,并且在server端设置的timeout为0,所以导致这些链接一直存活,不能断开,才会出现这些情况。
若有其中不正确的地方,请指正,自己也是菜鸟一枚.