探寻 Redis 内存诡异增长的元凶
一、现象
实例名:r-bp1cxxxxxxxxxd04(主从)
时间:2017-11-16 12:26~12:27
问题:一分钟内存上涨了2G,如下图所示:
键值规模:6000万左右
二、Redis内存分析
1. 内存组成
上图中的内存统计的是Redis的info memory命令中的used_memory属性,例如:
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 |
计算公式如下:
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
Sampled 67234427 keys in the keyspace!
Total key length in bytes is 1574032382 (avg len 23.41)
Biggest string found 'CCARD_DEVICE_CARD_REF_MAP_KEY_016817000004209' has 20862 bytes
Biggest list found 'CCARD_VALID_DEVICE_TRAIN_QUEUE_KEY' has 51 items
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找到明显问题,执行后发现:
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的情况
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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天过期时间int expireTime = 60 * 60 * 24;// rehash阈值 - 50为了方便观察rehash内存变化int rehashThreshold = (int) Math.pow(2, 25) - 50;// 1.批量写入:pipeline批量写入,由于是本机测试,这里用10000,实际生产不要这么用Pipeline pipeline = jedis.pipelined();
pipeline = jedis.pipelined();for (int i = 0; i < rehashThreshold; i++) {
pipeline.setex(String.valueOf(i), expireTime, String.valueOf(i)); if (i % 10000 == 0) {
pipeline.sync();
}
}
pipeline.sync();// 2.等待写增量TimeUnit.SECONDS.sleep(5);for (int i = rehashThreshold; i < rehashThreshold + 200; i++) {
jedis.setex(String.valueOf(i), expireTime, String.valueOf(i));
TimeUnit.SECONDS.sleep(1);
}
(2) 开始测试
(a) 当阈值=215=32768,从下面可以看出到key的个数为32769时,内存涨了一些,但是还不明显。
keys mem clients blocked requests connections32766 4.69M 3 0 32797 (+2) 4
32767 4.69M 3 0 32799 (+2) 4
32768 4.69M 3 0 32801 (+2) 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。
keys mem clients blocked requests connections1048574 128.69M 3 0 3364129 (+2) 16
1048575 128.69M 3 0 3364131 (+2) 16
1048576 128.69M 3 0 3364133 (+2) 16
1048577 160.69M 3 0 3364135 (+2) 16
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。
keys mem clients blocked requests connections67108862 9.70G 3 0 70473683 (+2) 18
67108863 9.70G 3 0 70473685 (+2) 18
67108864 9.70G 3 0 70473687 (+2) 18
67108865 11.70G 3 0 70473689 (+2) 18
67108866 11.70G 3 0 70473691 (+2) 18
67108867 11.70G 3 0 70473693 (+2) 18
回过来看r-bp1c15fd9b142d04的key和内存变化图,可以发现上面的规则是正确的:
4. 后续观察
17点时,rehash结束,内存降了增加的2G的一半。
五、总结
由于哈希表的特性,Redis 中键值数量大,不会对存取造成性能影响,但是会出现本文提到的问题。控制键个数有几个建议:无用的键值设置过期时间或者定期删除。优化键值设计:例如可以使用 ziplist hash合并优化部分字符串类型。未来改进:内核层面支持 rehash 的审计日志以及增强 rehash 的速度。
原文发布时间为:2018-09-25
本文作者:付磊
本文来自云栖社区合作伙伴“云时代架构”,了解相关信息可以关注“云时代架构”。
探寻 Redis 内存诡异增长的元凶相关推荐
- redis 内存不足 排查_Redis——内存占用优化
# 1.优化内存占用 了解redis的内存模型,对优化redis内存占用有很大帮助.下面介绍几种优化场景. - 1)利用jemalloc特性进行优化 上一小节所讲述的90000个键值便是一个例子.由于 ...
- 记录一次生产环境中Redis内存增长异常排查全流程!
作者:z小赵 ★ 一枚用心坚持写原创的"无趣"程序猿,在自身受益的同时也让朋友们在技术上有所提升. 最近 DBA 反馈线上的一个 Redis 资源已经超过了预先设计时的容量,并且已 ...
- 写一段代码提高内存占用_记录一次生产环境中Redis内存增长异常排查全流程!...
点击上方 IT牧场 ,选择 置顶或者星标 技术干货每日送达 最近 DBA 反馈线上的一个 Redis 资源已经超过了预先设计时的容量,并且已经进行了两次扩容,内存增长还在持续中,希望业务方排查一下容量 ...
- 内存分析_Redis内存爆炸增长?你需要知道这一套Redis内存分析方法
Redis Redis介绍 NoSQL Redis是当前比较热门的NOSQL数据库之一,和Memcache一样,数据都是缓存在计算机内存中.完全开源免费,遵守BSD协议,是一个高性能的key-valu ...
- 为什么Redis内存不宜过大
redis这个内存数据库,它的高性能.稳定性都是不用怀疑的,但我们塞进redis的数据过多,内存过大,那如果出问题,那它可能会带给我们的就是灾难性. 这几年的线上业务表明,redis这个内存数据库,它 ...
- Redis内存淘汰策略LRU、LFU详解
Redis内存淘汰原因 Redis是一种内存数据库,redis的容量往往有限,无法存放所有的数据.当内存满了的时候,并且这个时候还需要往Redis中放入新的数据,就需要将Redis中的一部分数据淘汰了 ...
- Redis内存淘汰机制
Redis内存淘汰机制 网趣科技 2016-09-06 13:26 摘要 Redis是一款优秀的.开源的内存数据库,我在阅读Redis源码实现的过程中,时时刻刻能感受到Redis作者为更好地使用内存而 ...
- Redis(四)Redis内存
文章目录 一.内存 1.1 内存消耗 1.1.1 内存使用统计 1.1.2 内存消耗划分 1.2 内存管理 1.2.1 设置内存上限 1.2.2 动态调整内存上限 1.2.3 键过期删除策略 1.2. ...
- redis学习笔记(5)之redis内存优化
redis内存优化 配置优化 Linux 配置优化 Redis配置优化 缩减键值对象 命令处理 缓存淘汰优化 动态改配置命令 设置最大内存 设置淘汰策略 内存淘汰策略 如何选择淘汰策略 内容来源为六星 ...
最新文章
- LeetCode简单题之和为零的N个唯一整数
- 磨刀不误砍柴功:App开发者必备之8大利器
- Meta小冰英伟达一起搞事!亚洲首个元宇宙生态联合体来了
- Python练习2-基本聊天程序-虚拟茶会话
- virtuoso根据原理图绘制版图并联接_版图绘制及Virtuoso软件工具使用.ppt
- C语言实现TEA系列加解密算法
- PHP的stdClass
- QQ春节游园会被拆开11.2亿个福袋 近一半都被00后给拆了
- Liferay教程– Liferay门户Portlet教程
- NOIP2018初赛 解题报告
- 内存陷阱 驯服C++中的野指针
- 安装appcan后打开eclipse出错
- 2021年游戏开发中的10大编程语言:C++、Java、C#......
- C语言实现J1939长帧组包接口以及模拟DM1数据并生成CANalyst数据文件
- App后台开发运维——架构设计
- 基于zigbee的智能家用空气监测系统
- java判断一个数是否为素数的程序_java如何判断一个数是否为素数
- 软件岗位--CTO、技术VP、技术总监、首席架构师
- 第九周 项目一--猴子选大王(数组版)
- 分布式调度框架大集合
热门文章
- 一个关于二叉树的创建、先序遍历、中序遍历、后序遍历、求叶子节点的完整函数的c语言完整程序。
- jdk读写锁ReentrantReadWriteLock
- html body不定宽居中,纯CSS实现元素垂直水平居中-非固定宽度
- 有没有词匹配算法_整站关键词SEO的匹配优化方法
- word中填充效果锁定纵横比_【文艺范】Word文档中的首字下沉效果
- PHP PSR4自动加载代码赏析
- 垃圾回收算法与实现系列-String在虚拟机中的实现
- linux pam 解锁_Linux 密码复杂度设置pam_pwquality、pam_passwdqc(centos7)
- kafka消费者分区消费策略
- FIREDAC用于LINUX报头文件FireDAC.VCLUI.Wait找不到