案例一:内存申请报错
解决redis连接错误:MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persi
安装redis后在设置过期时间时,突然报错:MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.
究其原因是因为强制把redis快照关闭了导致不能持久化的问题,在网上查了一些相关解决方案。
通过stop-writes-on-bgsave-error值设置为no即可避免这种问题。
有两种修改方法,一种是通过redis命令行修改,这种方式方便,直接,更改后直接生效,解决问题。
命令行修改方式示例: 127.0.0.1:6379> config set stop-writes-on-bgsave-error no
另一种是直接修改redis.conf配置文件,但是更改后需要重启redis。
修改redis.conf文件:
vi打开redis-server配置的redis.conf文件,然后使用快捷匹配模式:/ stop-writes-on-bgsave-error定位到stop-writes-on-bgsave-error字符串所在位置,接着把后面的yes设置为no即可。然后重启,redis-cli -h 127.0.0.1 -p 6379 shutdown。
也可以使用终极武器 kill -9。
案例二:Redis 2种持久化模式的缺陷
一、RDB持久化模式缺陷
1.问题描述:
并发200路,模拟不断写Redis,持续4小时后,接口调用开始出现大量失败,错误信息如下:
{"data":{"sendResult":null},"base":{"returncode":"99999","returndesc":"系统异常:MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error."},"qrybase":{"total":0,"count":0,"start":0}}
2.原因分析:
解读错误信息,以为是磁盘不够用引起,结果发现磁盘还剩余42%,如下所示:
具体原因:Redis在保存数据到硬盘时为了避免主进程假死,需要Fork一份主进程,然后在Fork进程内完成数据保存到硬盘的操作,如果主进程使用了2.2GB的内存,Fork子进程的时候需要额外的2.2GB,此时内存就不够了,Fork失败,进而数据保存硬盘也失败了。
3.缓解方案(不能根本解决问题):
3.1 修改redis.conf文件中配置项stop-writes-on-bgsave-error no (默认值为yes),即当bgsave快照操作出错时停止写数据到磁盘,这样后面写错做均会失败,为了不影响后续写操作,故需将该项值改为no
3.2 修改内核参数(如下3种方式),但需要root权限:
(1) 编辑/etc/sysctl.conf ,改vm.overcommit_memory=1,然后sysctl -p 使配置文件生效
(2)sysctl vm.overcommit_memory=1
(3)echo 1 > /proc/sys/vm/overcommit_memory
二、AOF持久化模式缺陷
1.问题1描述:
Redis主从节点均开启AOF模式,并发200路,模拟不断写Redis,持续15分钟后,接口调用开始出现大量失败,且Redis所在的Linux虚拟服务器挂起。
接口报错如下:
{"data":null,"base":{"returndesc":"系统异常","returncode":"999999"},"qrybase":null}
Biz(dubbo)接口报错如下:
2015-06-05 11:28:28.760 [DubboServerHandler-192.168.57.144:20882-thread-173] ERROR com.iflytek.diange.biz.redis.JedisFactory - error while validate jedis!
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
原因分析:
从dubbo接口报错信息来看,是由于接口API操作Redis超时导致。从系统日志和IO监控来看,均说明上述问题是由于IO瓶颈(系统IO过于繁忙)所致,如下所示:
从系统日志也能看出,IO阻塞时间超过了120秒,由于系统安全机制导致机器挂起。
总结
测试结果证明AOF模式存在最明显缺陷,即访问压力大时IO会成为性能瓶颈,进而导致服务不可用。
3.缓解方案(不能根本解决问题)
编辑/etc/sysctl.conf ,添加如下配置:
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
然后sysctl -p 使配置文件生效。
问题2描述:
无论采用AOF模式还是RDB(快照模式),当两文件(.aof或.rdb)大小超过系统内存80%,Redis进程会被系统Kill掉,导致服务不可用。
总结
上述问题说明我们在使用Redis时需要事先做好系统内存的容量规划,因为一旦Redis宕掉会导致大量数据丢失且是不可恢复的。
案例三:Redis内存碎片率
一、 内存碎片率
mem_fragmentation_ratio = used_memory_rss / used_memory
used_memory
:Redis使用其分配器分配的内存大小used_memory_rss
:操作系统分配给Redis实例的内存大小,表示该进程所占物理内存的大小
两者包括了实际缓存占用的内存和Redis自身运行所占用的内存,used_memory_rss指标还包含了内存碎片的开销,内存碎片是由操作系统低效的分配/回收物理内存导致的。
mem_fragmentation_ratio < 1 表示Redis内存分配超出了物理内存,操作系统正在进行内存交换,内存交换会引起非常明显的响应延迟;
mem_fragmentation_ratio > 1 是合理的;
mem_fragmentation_ratio > 1.5 说明Redis消耗了实际需要物理内存的150%以上,其中50%是内存碎片率,可能是操作系统或Redis实例中内存管理变差的表现
二、 内存碎片率高的原因
1.遇到变长key-value负载:存储的数据长短差异较大,频繁更新,redis的每个k-v对初始化的内存大小是最适合的,当修改的value改变的并且原来内存大小不适用的时候,就需要重新分配内存。重新分配之后,就会有一部分内存redis无法正常回收,一直占用着。
2.maxmemory限制导致key被回收删除
redis写入大量数据,这些数据的key和原来的数据很多不一致,数据超过maxmemory限制后redis会通过key的回收策略将部分旧数据淘汰,而被淘汰的数据本身占用的内存却没有被redis进程释放,导致redis内存的有效数据虽然没有超过最大内存,但是整个进程的内存在一直增长
info信息中的evicted_keys字段显示的是,因为maxmemory限制导致key被回收删除的数量
key经常需要回收,会使客户端命令响应延迟时间增加,因为Redis不但要处理客户端过来的命令请求,还要频繁的回收满足条件的key
三、 解决方法
限制内存交换: 如果内存碎片率低于1,Redis实例可能会把部分数据交换到硬盘上,应该增加可用物理内存或减少实Redis内存占用,设置maxmemory和回收策略可以避免强制内存交换
重启Redis服务器:如果内存碎片率超过1.5,重启Redis服务器可以让额外产生的内存碎片失效并重新作为新内存来使用,使操作系统恢复高效的内存管理。额外碎片的产生是由于Redis释放了内存块,但内存分配器并没有返回内存给操作系统
内存碎片清理:Redis 4.0-RC3 以上版本,使用jemalloc作为内存分配器(默认的) 支持内存碎片清理
支持在运行期进行自动内存碎片清理
设置自动清理 config set activedefrag yes
,使用config rewrite
将redis内存中新配置刷新到配置文件
支持通过命令 memory purge
进行手动清理(与自动清理区域不同)
redis4支持内存碎片清理功能使用
最近看到redis4支持内存碎片清理了, 之前一直期待有这么一个功能, 因为之前遇到内存碎片的解决办法就是重启, 现在终于有了优雅的解决方案.^o^/, 这个功能其实oranagra 在2017年1月1日已经提交pr了, 相关地址: https://github.com/antirez/redis/pull/3720
版本说明:
Redis 4.0-RC3 以上版本才支持的
需要使用jemalloc作为内存分配器(默认的)
功能介绍:
支持在运行期进行自动内存碎片清理 (config set activedefrag yes)
支持通过命令 memory purge 进行清理(与自动清理区域不同)
功能验证流程:
(1) 首先需要拉取4.0-RC3之后的版本代码, 编译
(2) 启动时限定内存大小为1g并启动lru, 命令如下:
./src/redis-server --maxmemory 1gb --maxmemory-policy allkeys-lru --activedefrag no --port 6383
(3) 构造大量数据并导致lru, 这样可以触发内存碎片, 命令如下:
redis-cli -p 6383 debug populate 7000000 asdf 150
(4) 查看当前的内存使用情况, 会发现有200多万的数据被清理掉了
$ redis-cli -p 6383 info keyspace
# Keyspace
db0:keys=4649543,expires=0,avg_ttl=0
(5) 查看当前的内存碎片率, 这时碎片率(mem_fragmentation_ratio)很高 : 1.54, 意味着54%的内存浪费
$ redis-cli -p 6383 info memory
# Memory
used_memory:1073741736
used_memory_human:1024.00M
used_memory_rss:1650737152
used_memory_rss_human:1.54G
used_memory_peak:1608721680
used_memory_peak_human:1.50G
used_memory_peak_perc:66.75%
used_memory_overhead:253906398
used_memory_startup:766152
used_memory_dataset:819835338
used_memory_dataset_perc:76.41%
total_system_memory:67535904768
total_system_memory_human:62.90G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:1073741824
maxmemory_human:1.00G
maxmemory_policy:allkeys-lru
mem_fragmentation_ratio:1.54
mem_allocator:jemalloc-4.0.3
active_defrag_running:0
lazyfree_pending_objects:0
(6) 看看内存分配的详细情况, 这个地方看不懂可以看看: 科普文, 关键的是util指标, 指的是内存利用率, 最大的bins内存util是0.661, 说明内存利用率不高
$ echo "`redis-cli -p 6383 memory malloc-stats`"
___ Begin jemalloc statistics ___
Version: 4.0.3-0-ge9192eacf8935e29fc62fddc2701f7942b1cc02c
Assertions disabled
Run-time option settings:
opt.abort: false
opt.lg_chunk: 21
opt.dss: "secondary"
opt.narenas: 48
opt.lg_dirty_mult: 3 (arenas.lg_dirty_mult: 3)
opt.stats_print: false
opt.junk: "false"
opt.quarantine: 0
opt.redzone: false
opt.zero: false
opt.tcache: true
opt.lg_tcache_max: 15
CPUs: 12
Arenas: 48
Pointer size: 8
Quantum size: 8
Page size: 4096
Min active:dirty page ratio per arena: 8:1
Maximum thread-cached size class: 32768
Chunk size: 2097152 (2^21)
Allocated: 1074509704, active: 1609732096, metadata: 41779072, resident: 1651118080, mapped: 1652555776
Current active ceiling: 1610612736
arenas[0]:
assigned threads: 1
dss allocation precedence: secondary
min active:dirty page ratio: 8:1
dirty pages: 393001:24 active:dirty, 0 sweeps, 0 madvises, 0 purged
allocated nmalloc ndalloc nrequests
small: 1006565256 28412640 9802493 35714594
large: 835584 20 11 20
huge: 67108864 1 0 1
total: 1074509704 28412661 9802504 35714615
active: 1609732096
mapped: 1650458624
metadata: mapped: 40202240, allocated: 491904
bins: size ind allocated nmalloc ndalloc nrequests curregs curruns regs pgs util nfills nflushes newruns reruns
8 0 1992 319 70 357 249 1 512 1 0.486 7 8 1 0
16 1 148618896 14110300 4821619 14310175 9288681 55119 256 1 0.658 141103 48825 55119 5
24 2 112104360 7200400 2529385 7300348 4671015 14064 512 3 0.648 72004 25881 14064 1
32 3 288 112 103 7003270 9 1 128 1 0.070 3 7 1 0
40 4 360 109 100 171 9 1 512 5 0.017 3 7 1 0
48 5 1248 112 86 63 26 1 256 3 0.101 2 5 1 0
56 6 896 106 90 16 16 1 512 7 0.031 2 6 1 0
64 7 128 64 62 5 2 1 64 1 0.031 1 3 1 0
80 8 880 106 95 7 11 1 256 5 0.042 2 4 1 0
96 9 9120 212 117 97 95 1 128 3 0.742 4 6 2 1
112 10 336 109 106 2 3 1 256 7 0.011 3 6 3 0
128 11 640 40 35 4 5 1 32 1 0.156 3 4 2 0
160 12 740617440 7000148 2371289 7000001 4628859 54688 128 5 0.661 70271 24334 54689 4
192 13 768 68 64 1 4 1 64 3 0.062 2 4 2 0
224 14 4683616 100000 79091 99946 20909 781 128 7 0.209 1000 1641 782 0
256 15 0 16 16 4 0 0 16 1 1 1 3 1 0
320 16 5120 64 48 16 16 1 64 5 0.250 1 3 1 0
384 17 768 33 31 2 2 1 32 3 0.062 1 3 1 0
448 18 28672 64 0 0 64 1 64 7 1 1 0 1 0
512 19 1024 10 8 4 2 1 8 1 0.250 1 2 2 0
640 20 0 32 32 1 0 0 32 5 1 1 3 1 0
---
896 22 48384 85 31 50 54 2 32 7 0.843 2 3 2 0
1024 23 3072 10 7 3 3 1 4 1 0.750 1 2 3 0
1280 24 20480 16 0 0 16 1 16 5 1 1 0 1 0
1536 25 15360 10 0 0 10 2 8 3 0.625 1 0 2 0
1792 26 28672 16 0 0 16 1 16 7 1 1 0 1 0
2048 27 4096 10 8 2 2 1 2 1 1 1 2 5 0
---
3584 30 35840 10 0 0 10 2 8 7 0.625 1 0 2 0
---
5120 32 250880 49 0 49 49 13 4 5 0.942 0 0 13 0
---
8192 35 81920 10 0 0 10 10 1 2 1 1 0 10 0
---
large: size ind allocated nmalloc ndalloc nrequests curruns
16384 39 16384 2 1 2 1
20480 40 40960 2 0 2 2
---
32768 43 32768 1 0 1 1
40960 44 40960 10 9 10 1
---
81920 48 81920 1 0 1 1
---
131072 51 131072 1 0 1 1
163840 52 163840 1 0 1 1
---
327680 56 327680 1 0 1 1
---
1048576 63 0 1 1 1 0
---
huge: size ind allocated nmalloc ndalloc nrequests curhchunks
---
67108864 87 67108864 1 0 1 1
---
--- End jemalloc statistics ---
(7) 开启自动内存碎片整理
$ redis-cli -p 6383 config set activedefrag yes
OK
(8) 等会儿再看看, 发现内存碎片降低了
$ redis-cli -p 6383 info memory
# Memory
used_memory:1073740712
used_memory_human:1024.00M
used_memory_rss:1253371904
used_memory_rss_human:1.17G
used_memory_peak:1608721680
used_memory_peak_human:1.50G
used_memory_peak_perc:66.74%
used_memory_overhead:253906398
used_memory_startup:766152
used_memory_dataset:819834314
used_memory_dataset_perc:76.41%
total_system_memory:67535904768
total_system_memory_human:62.90G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:1073741824
maxmemory_human:1.00G
maxmemory_policy:allkeys-lru
mem_fragmentation_ratio:1.17
mem_allocator:jemalloc-4.0.3
active_defrag_running:0
lazyfree_pending_objects:0
(9) 可以再看看内存利用率, 可以看到已经上升到0.82
$ echo "`redis-cli -p 6383 memory malloc-stats`"
___ Begin jemalloc statistics ___
Version: 4.0.3-0-ge9192eacf8935e29fc62fddc2701f7942b1cc02c
Assertions disabled
Run-time option settings:
opt.abort: false
opt.lg_chunk: 21
opt.dss: "secondary"
opt.narenas: 48
opt.lg_dirty_mult: 3 (arenas.lg_dirty_mult: 3)
opt.stats_print: false
opt.junk: "false"
opt.quarantine: 0
opt.redzone: false
opt.zero: false
opt.tcache: true
opt.lg_tcache_max: 15
CPUs: 12
Arenas: 48
Pointer size: 8
Quantum size: 8
Page size: 4096
Min active:dirty page ratio per arena: 8:1
Maximum thread-cached size class: 32768
Chunk size: 2097152 (2^21)
Allocated: 1074509800, active: 1307602944, metadata: 41779072, resident: 1512247296, mapped: 1652555776
Current active ceiling: 1308622848
arenas[0]:
assigned threads: 1
dss allocation precedence: secondary
min active:dirty page ratio: 8:1
dirty pages: 319239:39882 active:dirty, 4878 sweeps, 6343 madvises, 33915 purged
allocated nmalloc ndalloc nrequests
small: 1006565352 35456589 16846439 45126633
large: 835584 24 15 24
huge: 67108864 1 0 1
total: 1074509800 35456614 16846454 45126658
active: 1307602944
mapped: 1650458624
metadata: mapped: 40202240, allocated: 491904
bins: size ind allocated nmalloc ndalloc nrequests curregs curruns regs pgs util nfills nflushes newruns reruns
8 0 1992 319 70 357 249 1 512 1 0.486 7 8 1 0
16 1 148618896 17658482 8369801 17858357 9288681 44332 256 1 0.818 141103 48825 55119 26364
24 2 112104360 8897298 4226283 8997246 4671015 11525 512 3 0.791 72004 25881 14064 6205
32 3 384 115 103 9371363 12 1 128 1 0.093 4 7 1 0
40 4 360 109 100 171 9 1 512 5 0.017 3 7 1 0
48 5 1248 112 86 63 26 1 256 3 0.101 2 5 1 0
56 6 896 106 90 16 16 1 512 7 0.031 2 6 1 0
64 7 128 64 62 5 2 1 64 1 0.031 1 3 1 0
80 8 880 106 95 7 11 1 256 5 0.042 2 4 1 0
96 9 9120 212 117 97 95 1 128 3 0.742 4 6 2 1
112 10 336 109 106 2 3 1 256 7 0.011 3 6 3 0
128 11 640 40 35 4 5 1 32 1 0.156 3 4 2 0
160 12 740617440 8788058 4159199 8787911 4628859 44056 128 5 0.820 70271 24334 54689 26488
192 13 768 68 64 1 4 1 64 3 0.062 2 4 2 0
224 14 4683616 110956 90047 110902 20909 467 128 7 0.349 1000 1641 782 105
256 15 0 16 16 4 0 0 16 1 1 1 3 1 0
320 16 5120 64 48 16 16 1 64 5 0.250 1 3 1 0
384 17 768 33 31 2 2 1 32 3 0.062 1 3 1 0
448 18 28672 64 0 0 64 1 64 7 1 1 0 1 0
512 19 1024 10 8 4 2 1 8 1 0.250 1 2 2 0
640 20 0 32 32 1 0 0 32 5 1 1 3 1 0
---
896 22 48384 85 31 50 54 2 32 7 0.843 2 3 2 0
1024 23 3072 10 7 3 3 1 4 1 0.750 1 2 3 0
1280 24 20480 16 0 0 16 1 16 5 1 1 0 1 0
1536 25 15360 10 0 0 10 2 8 3 0.625 1 0 2 0
1792 26 28672 16 0 0 16 1 16 7 1 1 0 1 0
2048 27 4096 10 8 2 2 1 2 1 1 1 2 5 0
---
3584 30 35840 10 0 0 10 2 8 7 0.625 1 0 2 0
---
5120 32 250880 49 0 49 49 13 4 5 0.942 0 0 13 0
---
8192 35 81920 10 0 0 10 10 1 2 1 1 0 10 0
---
large: size ind allocated nmalloc ndalloc nrequests curruns
16384 39 16384 2 1 2 1
20480 40 40960 2 0 2 2
---
32768 43 32768 1 0 1 1
40960 44 40960 14 13 14 1
---
81920 48 81920 1 0 1 1
---
131072 51 131072 1 0 1 1
163840 52 163840 1 0 1 1
---
327680 56 327680 1 0 1 1
---
1048576 63 0 1 1 1 0
---
huge: size ind allocated nmalloc ndalloc nrequests curhchunks
---
67108864 87 67108864 1 0 1 1
---
--- End jemalloc statistics ---
(10) 别急, 还有一个大招: 手动清理
$ redis-cli -p 6383 memory purge
(11) 再次查看内存使用情况: 发现碎片率降到1.04, 内存利用率到0.998, 内存碎片基本上消灭了^_^
配置说明:
# Enabled active defragmentation
# 碎片整理总开关
# activedefrag yes
# Minimum amount of fragmentation waste to start active defrag
# 内存碎片达到多少的时候开启整理
active-defrag-ignore-bytes 100mb
# Minimum percentage of fragmentation to start active defrag
# 碎片率达到百分之多少开启整理
active-defrag-threshold-lower 10
# Maximum percentage of fragmentation at which we use maximum effort
# 碎片率小余多少百分比开启整理
active-defrag-threshold-upper 100
# Minimal effort for defrag in CPU percentage
active-defrag-cycle-min 25
# Maximal effort for defrag in CPU percentage
active-defrag-cycle-max 75
总结:
从测试的结果看, 效果还是非常不错的, 另外在配置中我们可以看到如下一段声明:
说明现在这个功能还是实验性质的, 对应的命令在官方文档中都没有看到. 但是它也说经过了压力测试, 而且现在也一年多了, 经受了一些考验, 可以尝试小流量上线观察
TODO redis 内存碎片整理实现