一、现象

  • 实例名:r-bp1cxxxxxxxxxd04(主从)

  • 时间:2017-11-16 12:26~12:27

  • 问题:一分钟内存上涨了2G,如下图所示:

  • 键值规模:6000万左右

二、Redis内存分析

1. 内存组成

上图中的内存统计的是Redis的info memory命令中的used_memory属性,例如:

  1. redis> info memory# Memoryused_memory:9195978072used_memory_human:8.56Gused_memory_rss:9358786560used_memory_peak:10190212744used_memory_peak_human:9.49Gused_memory_lua:38912mem_fragmentation_ratio:1.02mem_allocator:jemalloc-3.6.0

每个属性的详细说明

属性名 属性说明
used_memory Redis 分配器分配的内存量,也就是实际存储数据的内存总量
used_memory_human 以可读格式返回 Redis 使用的内存总量
used_memory_rss 从操作系统的角度,Redis进程占用的总物理内存
used_memory_peak 内存分配器分配的最大内存,代表used_memory的历史峰值
used_memory_peak_human 以可读的格式显示内存消耗峰值
used_memory_lua Lua引擎所消耗的内存
mem_fragmentation_ratio used_memory_rss /used_memory比值,表示内存碎片率
mem_allocator Redis 所使用的内存分配器。默认: jemalloc

计算公式如下:

  1. used_memory = 自身内存+对象内存+缓冲内存+lua内存used_rss = used_memory + 内存碎片

如下图所示:

2. 内存分析

(1) 自身内存:一个空的Redis占用很小,可以忽略不计
(2) kv内存:key对象 + value对象
(3) 缓冲区:客户端缓冲区(普通 + slave伪装 + pubsub)以及aof缓冲区(比较固定,一般没问题)
(4) Lua:Lua引擎所消耗的内存

3. 内存突增常见问题

(1) kv内存:bigkey、大量写入
(2) 客户端缓冲区:一般常见的有普通客户端缓冲区(例如monitor命令)或者pubsub客户端缓冲区

三、问题排查

(1) bigkey ? 经扫描未发现bigkey

  1. Sampled 67234427 keys in the keyspace!

  2. Total key length in bytes is 1574032382 (avg len 23.41)

  3. Biggest string found 'CCARD_DEVICE_CARD_REF_MAP_KEY_016817000004209' has 20862 bytes

  4. Biggest   list found 'CCARD_VALID_DEVICE_TRAIN_QUEUE_KEY' has 51 items

  5. Biggest   hash found 'CCARD_VALID_DEVICE_TRAIN_MAP_KEY' has 51 fields67234359 strings with 71767890 bytes (100.00% of keys, avg size 1.07)67 lists with 151 items (00.00% of keys, avg size 2.25)0 sets with 0 members (00.00% of keys, avg size 0.00)1 hashs with 51 fields (00.00% of keys, avg size 51.00)0 zsets with 0 members (00.00% of keys, avg size 0.00)

(2) 键值个数增加?未发现键值有明显变化

(3) 客户端缓冲区

由于内存增上去后,长时间没下落,如果是因为缓冲区问题,会从info clients找到明显问题,执行后发现:

  1. redis> info clients# Clientsconnected_clients:43client_longest_output_list:0client_biggest_input_buf:0blocked_clients:0admin_clients:6rejected_vpc_conn_count:0close_idle_unknown_conn_count:0

执行client中也没有明显的omem大于0的情况

  1. id=80207 addr=10.xx.0.4:63920 fd=46 name= age=624 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  2. id=80215 addr=10.xx.0.23:43489 fd=36 name= age=591 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  3. id=80366 addr=10.xx.0.8:59785 fd=18 name= age=84 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=del read=0 write=0 type=user

  4. id=80356 addr=10.xx.0.33:32117 fd=13 name= age=114 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  5. id=80064 addr=10.xx.59.4:53446 fd=38 name= age=1070 idle=1070 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL read=0 write=0 type=admin

  6. id=80276 addr=10.xx.0.23:48511 fd=8 name= age=387 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  7. id=80188 addr=10.xx.0.33:16265 fd=42 name= age=681 idle=3 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  8. id=80326 addr=10.xx.0.32:59779 fd=16 name= age=209 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  9. id=80065 addr=10.xx.59.4:53447 fd=45 name= age=1070 idle=1070 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL read=0 write=0 type=admin

  10. id=79936 addr=10.xx.0.22:10607 fd=30 name= age=1480 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  11. id=80174 addr=10.xx.0.5:60914 fd=6 name= age=722 idle=2 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  12. id=80300 addr=10.xx.0.22:22757 fd=48 name= age=298 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  13. id=80037 addr=10.xx.0.5:55189 fd=15 name= age=1143 idle=2 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  14. id=80330 addr=10.xx.0.8:48533 fd=17 name= age=199 idle=10 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  15. id=79896 addr=10.xx.0.30:26814 fd=11 name= age=1616 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  16. id=80299 addr=10.xx.0.24:11227 fd=44 name= age=303 idle=3 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  17. id=80086 addr=10.xx.0.32:52526 fd=40 name= age=1002 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  18. id=80202 addr=10.xx.0.33:16658 fd=26 name= age=636 idle=3 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  19. id=80256 addr=10.xx.0.24:60496 fd=19 name= age=448 idle=2 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  20. id=79908 addr=10.xx.0.29:18975 fd=12 name= age=1583 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  21. id=80365 addr=10.xx.0.29:46429 fd=14 name= age=85 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  22. id=79869 addr=10.xx.27.4:48455 fd=35 name= age=1700 idle=1700 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL read=0 write=0 type=admin

  23. id=80334 addr=10.xx.0.23:50012 fd=39 name= age=189 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  24. id=80041 addr=10.xx.0.32:51107 fd=33 name= age=1132 idle=3 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  25. id=79992 addr=10.xx.0.22:12068 fd=28 name= age=1289 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  26. id=80251 addr=10.xx.0.30:44213 fd=23 name= age=468 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  27. id=80006 addr=10.xx.0.2:45895 fd=31 name= age=1242 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  28. id=80321 addr=10.xx.0.30:48048 fd=5 name= age=224 idle=3 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  29. id=80381 addr=10.xx.0.8:13360 fd=22 name= age=24 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=del read=0 write=0 type=user

  30. id=80200 addr=10.xx.0.24:59183 fd=24 name= age=640 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  31. id=80113 addr=10.xx.0.2:52492 fd=21 name= age=915 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  32. id=174 addr=11.216.117.242:53027 fd=9 name= age=281390 idle=0 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=replconf read=0 write=0 type=admin

  33. id=79991 addr=10.xx.0.4:48412 fd=25 name= age=1296 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  34. id=80301 addr=127.0.0.1:47869 fd=49 name= age=291 idle=261 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=strlen read=0 write=0 type=admin

  35. id=80047 addr=10.xx.59.4:53184 fd=41 name= age=1114 idle=1114 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL read=0 write=0 type=admin

  36. id=80236 addr=10.xx.0.5:62546 fd=47 name= age=516 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  37. id=80364 addr=10.xx.0.4:18794 fd=7 name= age=85 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  38. id=80175 addr=10.xx.0.4:62245 fd=29 name= age=718 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  39. id=80336 addr=10.xx.0.29:45701 fd=50 name= age=180 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  40. id=80050 addr=10.xx.59.4:53188 fd=43 name= age=1114 idle=1114 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL read=0 write=0 type=admin

  41. id=79765 addr=10.xx.0.2:33832 fd=37 name= age=2027 idle=177 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=info read=0 write=0 type=user

  42. id=80170 addr=10.xx.0.2:57853 fd=20 name= age=728 idle=24 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping read=0 write=0 type=user

  43. id=80390 addr=127.0.0.1:49449 fd=27 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client read=0 write=0 type=admin

四、揪出元凶

常用的几招都用了,还是不行,同事@径远帮忙一起分析,怀疑是不是因为Redis的kv哈希表做了 rehash。

1. Redis的kv存储结构

如下图所示,Redis的所有kv保存在dict中,其中ht对应两个哈希表ht[0]和ht[1],平时一个空闲,一个用于存储数据,只有当需要rehash时,ht[1]才会用到。

2. Redis的字典rehash

为了保证哈希表的负载,当哈希表的元素个数等于哈希表槽数时候,会进行rehash扩容。扩容后h[1]的容量等于第一个大于等于ht[0].size*2的2n,例如hash表的初始化容量是4,那么下一次扩容就是8,以此类推。

3. 测试

(1) 测试方法

先批量写入到rehash阈值附近,然后在逐条去写,观察内存变化

  1. // 为每个键设置1天过期时间int expireTime = 60 * 60 * 24;// rehash阈值 - 50为了方便观察rehash内存变化int rehashThreshold = (int) Math.pow(2, 25) - 50;// 1.批量写入:pipeline批量写入,由于是本机测试,这里用10000,实际生产不要这么用Pipeline pipeline = jedis.pipelined();

  2. pipeline = jedis.pipelined();for (int i = 0; i < rehashThreshold; i++) {

  3.    pipeline.setex(String.valueOf(i), expireTime, String.valueOf(i));    if (i % 10000 == 0) {

  4.        pipeline.sync();

  5.    }

  6. }

  7. pipeline.sync();// 2.等待写增量TimeUnit.SECONDS.sleep(5);for (int i = rehashThreshold; i < rehashThreshold + 200; i++) {

  8.    jedis.setex(String.valueOf(i), expireTime, String.valueOf(i));

  9.    TimeUnit.SECONDS.sleep(1);

  10. }

(2) 开始测试

(a) 当阈值=215=32768,从下面可以看出到key的个数为32769时,内存涨了一些,但是还不明显。

  1. keys       mem      clients blocked requests            connections32766      4.69M    3       0       32797 (+2)          4

  2. 32767      4.69M    3       0       32799 (+2)          4

  3. 32768      4.69M    3       0       32801 (+2)          4

  4. 32769      5.44M    3       0       32803 (+2)          4

(b) 当阈值=220=1048576,从下面可以看出到key的个数为1048577时,内存涨了32M。因为rehash会扩容,所以新的哈希表中的槽位变为了221 * 2(因为每个key都设置了过期时间,expires表),指针为8个字节,221 ️ 2 ️ 8 = 225 = 32MB。

  1. keys       mem      clients blocked requests            connections1048574    128.69M  3       0       3364129 (+2)        16

  2. 1048575    128.69M  3       0       3364131 (+2)        16

  3. 1048576    128.69M  3       0       3364133 (+2)        16

  4. 1048577    160.69M  3       0       3364135 (+2)        16

  5. 1048578    160.69M  3       0       3364137 (+2)        16

(c) 当阈值=226=67108864,从下面可以看出到key的个数为67108865时,内存涨了2GB。因为rehash会扩容,所以新的哈希表中的槽位变为了227 * 2(因为每个key都设置了过期时间,expires表),指针为8个字节,227 ️ 2 ️ 8 = 231 = 2GB。

  1. keys       mem      clients blocked requests            connections67108862   9.70G    3       0       70473683 (+2)       18

  2. 67108863   9.70G    3       0       70473685 (+2)       18

  3. 67108864   9.70G    3       0       70473687 (+2)       18

  4. 67108865   11.70G   3       0       70473689 (+2)       18

  5. 67108866   11.70G   3       0       70473691 (+2)       18

  6. 67108867   11.70G   3       0       70473693 (+2)       18

回过来看r-bp1c15fd9b142d04的key和内存变化图,可以发现上面的规则是正确的:

4. 后续观察

17点时,rehash结束,内存降了增加的2G的一半。

五、总结

由于哈希表的特性,Redis 中键值数量大,不会对存取造成性能影响,但是会出现本文提到的问题。控制键个数有几个建议:无用的键值设置过期时间或者定期删除。优化键值设计:例如可以使用 ziplist hash合并优化部分字符串类型。未来改进:内核层面支持 rehash 的审计日志以及增强 rehash 的速度。

原文发布时间为:2018-09-25

本文作者:付磊

本文来自云栖社区合作伙伴“云时代架构”,了解相关信息可以关注“云时代架构”。

探寻 Redis 内存诡异增长的元凶相关推荐

  1. redis 内存不足 排查_Redis——内存占用优化

    # 1.优化内存占用 了解redis的内存模型,对优化redis内存占用有很大帮助.下面介绍几种优化场景. - 1)利用jemalloc特性进行优化 上一小节所讲述的90000个键值便是一个例子.由于 ...

  2. 记录一次生产环境中Redis内存增长异常排查全流程!

    作者:z小赵 ★ 一枚用心坚持写原创的"无趣"程序猿,在自身受益的同时也让朋友们在技术上有所提升. 最近 DBA 反馈线上的一个 Redis 资源已经超过了预先设计时的容量,并且已 ...

  3. 写一段代码提高内存占用_记录一次生产环境中Redis内存增长异常排查全流程!...

    点击上方 IT牧场 ,选择 置顶或者星标 技术干货每日送达 最近 DBA 反馈线上的一个 Redis 资源已经超过了预先设计时的容量,并且已经进行了两次扩容,内存增长还在持续中,希望业务方排查一下容量 ...

  4. 内存分析_Redis内存爆炸增长?你需要知道这一套Redis内存分析方法

    Redis Redis介绍 NoSQL Redis是当前比较热门的NOSQL数据库之一,和Memcache一样,数据都是缓存在计算机内存中.完全开源免费,遵守BSD协议,是一个高性能的key-valu ...

  5. 为什么Redis内存不宜过大

    redis这个内存数据库,它的高性能.稳定性都是不用怀疑的,但我们塞进redis的数据过多,内存过大,那如果出问题,那它可能会带给我们的就是灾难性. 这几年的线上业务表明,redis这个内存数据库,它 ...

  6. Redis内存淘汰策略LRU、LFU详解

    Redis内存淘汰原因 Redis是一种内存数据库,redis的容量往往有限,无法存放所有的数据.当内存满了的时候,并且这个时候还需要往Redis中放入新的数据,就需要将Redis中的一部分数据淘汰了 ...

  7. Redis内存淘汰机制

    Redis内存淘汰机制 网趣科技 2016-09-06 13:26 摘要 Redis是一款优秀的.开源的内存数据库,我在阅读Redis源码实现的过程中,时时刻刻能感受到Redis作者为更好地使用内存而 ...

  8. Redis(四)Redis内存

    文章目录 一.内存 1.1 内存消耗 1.1.1 内存使用统计 1.1.2 内存消耗划分 1.2 内存管理 1.2.1 设置内存上限 1.2.2 动态调整内存上限 1.2.3 键过期删除策略 1.2. ...

  9. redis学习笔记(5)之redis内存优化

    redis内存优化 配置优化 Linux 配置优化 Redis配置优化 缩减键值对象 命令处理 缓存淘汰优化 动态改配置命令 设置最大内存 设置淘汰策略 内存淘汰策略 如何选择淘汰策略 内容来源为六星 ...

最新文章

  1. LeetCode简单题之和为零的N个唯一整数
  2. 磨刀不误砍柴功:App开发者必备之8大利器
  3. Meta小冰英伟达一起搞事!亚洲首个元宇宙生态联合体来了
  4. Python练习2-基本聊天程序-虚拟茶会话
  5. virtuoso根据原理图绘制版图并联接_版图绘制及Virtuoso软件工具使用.ppt
  6. C语言实现TEA系列加解密算法
  7. PHP的stdClass
  8. QQ春节游园会被拆开11.2亿个福袋 近一半都被00后给拆了
  9. Liferay教程– Liferay门户Portlet教程
  10. NOIP2018初赛 解题报告
  11. 内存陷阱 驯服C++中的野指针
  12. 安装appcan后打开eclipse出错
  13. 2021年游戏开发中的10大编程语言:C++、Java、C#......
  14. C语言实现J1939长帧组包接口以及模拟DM1数据并生成CANalyst数据文件
  15. App后台开发运维——架构设计
  16. 基于zigbee的智能家用空气监测系统
  17. java判断一个数是否为素数的程序_java如何判断一个数是否为素数
  18. 软件岗位--CTO、技术VP、技术总监、首席架构师
  19. 第九周 项目一--猴子选大王(数组版)
  20. 分布式调度框架大集合

热门文章

  1. 一个关于二叉树的创建、先序遍历、中序遍历、后序遍历、求叶子节点的完整函数的c语言完整程序。
  2. jdk读写锁ReentrantReadWriteLock
  3. html body不定宽居中,纯CSS实现元素垂直水平居中-非固定宽度
  4. 有没有词匹配算法_整站关键词SEO的匹配优化方法
  5. word中填充效果锁定纵横比_【文艺范】Word文档中的首字下沉效果
  6. PHP PSR4自动加载代码赏析
  7. 垃圾回收算法与实现系列-String在虚拟机中的实现
  8. linux pam 解锁_Linux 密码复杂度设置pam_pwquality、pam_passwdqc(centos7)
  9. kafka消费者分区消费策略
  10. FIREDAC用于LINUX报头文件FireDAC.VCLUI.Wait找不到