一. 我们怎么观察线上Redis内存
我们如果在我们的redis.conf配置文件里面配置:
# 最大内存设置,100M
maxmemory 104857600
查看redis内存信息 (info Memory)
172.29.2.11:7002> info Memory
# Memory
used_memory:2598042
used_memory_human:2.48M
used_memory_rss:3002368
used_memory_rss_human:2.86M
maxmemory:104857600
maxmemory_human:100.00M
mem_fragmentation_ratio:1.16
....
先大致看下几个字段的概念:
- 1. maxmemory
用户配置的(maxmemory )最大内存量, maxmemory_human 人能看懂的单位。
- 2. used_memory
Redis使用的内存总量,它包含了实际缓存占用的内存(包含虚拟内存)和Redis自身运行所占用的内存(如元数据、lua)。它是由Redis使用内存分配器分配的内存,所以这个数据并没有把内存碎片的内存给统计进去。
- 3. used_memory_rss
从操作系统角度看redis进程占用的内存量。包括进程运行本身需要的内存、内存碎片等,但是不包括虚拟内存。
- 4. mem_fragmentation_ratio
used_memory_rss/used_memory的值,可以代表碎片化。
mem_fragmentation_ratio越大,used_memory_rss就越大,内存碎片就越大。
mem_fragmentation_ratio小于1时,代表使用了过多的虚拟内存,由于虚拟内存的媒介是磁盘,比内存速度要慢很多,当这种情况出现时,应该及时排查,如果内存不足应该及时处理,如增加Redis节点、增加Redis服务器的内存、优化应用等。
那问题来了,内存碎片是什么?怎么产生的? 虚拟内存又是什么?如何能产生?
带着这些问题,我又薅掉了些自己仅存的几根头发,又欣喜的瞄了下对面老大的光头,并发誓我一定不会变成这样!就是带着这伟大的理想,我做了下面的事情。
二. Redis内存碎片实战
首先我配置maxmemory为100M ,然后写程序一直set key value 到 内存爆满后(注意是不同的key),直到程序抛出OOM异常:
然后我查看了下此时内存信息
used_memory_rss和used_memory 都达到了100M,证明内存已经打满了。
但是我们的mem_fragmentation_ratio 还很正常,于是我执行了下flushdb,删除当前数据库所有key。
172.29.2.10:7000> flushdb
OK
172.29.2.10:7000> info Memory
# Memory
used_memory:2617096
used_memory_human:2.50M
used_memory_rss:112009216
used_memory_rss_human:106.82M
...
mem_fragmentation_ratio:42.80
然后惊奇的发现: used_memory被释放了,但是used_memory_rss 还是雷打不动。mem_fragmentation_ratio内存碎片比达到了42.8之多,碎片化很严重。如果此时不清理掉碎片, 会导致redis重新设置大key时没法存放,这很严重。
Redis为什么这么做?
Redis有自己的内存分配器(jemalloc),当数据删除后,释放的内存空间由Redis自己的内存分配器管理,并没有立即将内存返回给操作系统,所以对于操作系统而言,仍然认为Redis占用了内存。这样的好处是,减少Redis向系统申请内存分配的次数,提升Redis自身性能。
jemalloc简单介绍
Redis在编译时便会指定内存分配器;内存分配器可以是 libc 、jemalloc或者tcmalloc, 默认是jemalloc。
jemalloc 内存分配方式为 按照一系列固定大小分配内存空间,jemalloc 按照申请的内存大小分配最接近的内存空间;
比如申请220字节,jemalloc 会分配256字节,如果还要继续写入20字节,Redis则不会继续向系统申请内存空间,直接写。
总而言之 : 按页分配内存,而不是按实际数据大小来分配,碎片产生的原因就是页内部分数据回收了,这个页还是占着空间。
三. Redis使用虚拟内存实战
首先我们在redis.conf配置文件配置虚拟内存:
# #开启虚拟内存
vm-enabled yes
# #交换出来的value保存的文件路径
vm-swap-file /usr/local/app/redis-cluster/7002/redis.swap
# #redis使用的最大内存上限
vm-max-memory 134857600
然后启动居然报错了:
*** FATAL CONFIG FILE ERROR ***
Reading the configuration file, at line 26
>>> 'vm-enabled yes'
Bad directive or wrong number of arguments
最后发现,据说redis2.6后面的版本已经把虚拟内存配置去掉了。~~~
此时,仿佛看到了一位不负责的男一号,疯狂的肆虐着女一号,女二号,女三号.... 为啥后面版本去掉了,我也不清除,我就简单介绍下虚拟内存思想吧:
redis 发现大部分数据都是冷数据(不常访问的数据),于是搞出虚拟内存来专门存放这些冷数据,这样就可以大大节省实际内存空间。 但是redis虚拟内存只是存储value,而不存储key。
好了我就知道这么多,即使你用虎头铡 我也只能言尽了~
四. Redis OOM
在上面有个环节,redis报了oom,原因就是使用的内存已经超过了配置的maxmemory。
## 客户端报错
OOM command not allowed when used memory > ‘maxmemory’
此时redis进程还是在的,并没有崩溃。如果在设置值也可能会设置进去,取决与下面这个配置:
maxmemory-policy volatile-lru
maxmemory-policy 为 内存达到峰值后,淘汰现有key,以便接受新数据 的策略,有下面几个可选参数:
- noeviction: 不淘汰现有key,直接返回异常,不再接受set key了。
- allkeys-lru: 优先删除掉最近最不经常使用的key,用以保存新数据。
- volatile-lru: 删除 最近不常使用的 且 设置了过期时间的key 。
- allkeys-random