Redis key 很少但是used_memory很大

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis key 很少但是used_memory很大相关的知识,希望对你有一定的参考价值。

    环境: 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,所以导致这些链接一直存活,不能断开,才会出现这些情况。




    若有其中不正确的地方,请指正,自己也是菜鸟一枚.

以上是关于Redis key 很少但是used_memory很大的主要内容,如果未能解决你的问题,请参考以下文章

Redis的内存和实现机制

LinkSLA智能运维技术派-Redis的监控

Redis性能问题排查解决手册

怎么查看redis使用情况?

万字总结,Redis 性能问题排查解决手册(强烈建议收藏)

关于redis内存分析,内存优化