前面写的文章redis数据迁移,在迁移过程中,明明做了数据删除,删除的数据量已是好几个G,但我使用 top 命令查看redis进程时,发现 Redis 还是占用了很多内存,有点懵,怀疑是不是删除失败了。连上redis,查看删除的key是否存在,一查的确删除的数据已经不在redis里面的,那是为什么呢?
查询了下官方文档,当数据删除后,Redis 释放的内存空间会由内存分配器管理,并不会立即返回给操作系统。所以,操作系统仍然会记录着给 Redis 分配了大量内存。
Redis 释放的内存空间可能并不是连续的,就会产生内存碎片,那么,这些不连续的内存空间很有可能处于一种闲置的状态。这就会导致一个问题:虽然有空闲空间,Redis 却无法用来保存数据,不仅会减少 Redis 能够实际保存的数据量,还会降低 Redis 运行机器的成本回报率。
通常情况下,内存空间闲置,往往是因为操作系统发生了较为严重的内存碎片。那么,什么是内存碎片呢?
举例:
我向操作系统申请内存,操作系统为我分配了 32 字节的连续内存空间,我的数据只需要24字节的内存空间就够了,还剩8字节的内存空间。后续我再存储24字节的数据,空间不够了。再向系统申请内存空间,系统再次分配给我32字节的内存空间,再次使用了24字节的连续内存空间,那剩余的两个 8字节的内存空间,如果后续没有被使用,就可以被称为内存碎片
Redis 存储存储数据的时候向操作系统申请的内存空间可能会大于数据实际需要的存储空间
内存分配器的分配策略就决定了操作系统无法做到“按需分配”。这是因为,内存分配器一般是按固定大小来分配内存,而不是完全按照应用程序申请的内存空间大小给程序分配。
比如:我这次value是100个字节,那就申请100个字节,系统不会分配100个字节,而是按照固定的大小去申请,为什么?由redis的策略所决定。
Redis 可以使用 libc、jemalloc、tcmalloc 多种内存分配器来分配内存,默认使用 jemalloc。接下来,我就以 jemalloc 为例,来具体解释一下。其他分配器也存在类似的问题。
jemalloc 划分的内存单元如下图所示:
jemalloc 的分配策略之一,是按照一系列固定的大小划分内存空间,例如 8 字节、16 字节、32 字节、48 字节,…, 2KB、4KB、8KB 等。当程序申请的内存最接近某个固定值时,jemalloc 会给它分配相应大小的空间。这样的分配方式本身是为了减少分配次数。例如,Redis 申请一个 20 字节的空间保存数据,jemalloc 就会分配 32 字节,此时,如果应用还要写入 10 字节的数据,Redis 就不用再向操作系统申请空间了,因为刚才分配的 32 字节已经够用了,这就避免了一次分配操作。
jemalloc内存分配器只能按固定大小分配内存,所以,分配的内存空间一般都会比申请的空间大一些,不会完全一致,这本身就会造成一定的碎片,降低内存空间存储效率。
比如说,应用保存 6 字节数据,jemalloc 按分配策略分配8字节。如果应用不再保存新数据,那么,这里多出来的 2 字节空间就是内存碎片了
查看redis官方文档内存优化,原文如下:
删除的键值对就不再需要内存空间了,此时,就会把空间释放出来,形成空闲空间。
redis使用 info memory
命令即可查看 Redis 内存相关的信息
指标 | 含义 |
---|---|
used_memory | 由 Redis 分配器分配的内存总量,包含了redis进程内部的开销和数据占用的内存,以字节(byte)为单位,即当前redis使用内存大小。 |
used_memory_human | 已更直观的单位展示分配的内存总量。 |
used_memory_rss | 向操作系统申请的内存大小,与 top 、 ps等命令的输出一致,即redis使用的物理内存大小。 |
used_memory_rss_human | 已更直观的单位展示向操作系统申请的内存大小。 |
used_memory_peak | redis的内存消耗峰值(以字节为单位),即历史使用记录中redis使用内存峰值。 |
used_memory_peak_human | 以更直观的格式返回redis的内存消耗峰值 |
used_memory_peak_perc | 使用内存达到峰值内存的百分比,used_memory/ used_memory_peak) 100%,即当前redis使用内存/历史使用记录中redis使用内存峰值100% |
used_memory_overhead | Redis为了维护数据集的内部机制所需的内存开销,包括所有客户端输出缓冲区、查询缓冲区、AOF重写缓冲区和主从复制的backlog。 |
used_memory_startup | Redis服务器启动时消耗的内存 |
used_memory_dataset | 数据实际占用的内存大小,即used_memory-used_memory_overhead |
used_memory_dataset_perc | 数据占用的内存大小的百分比,100%*(used_memory_dataset/(used_memory-used_memory_startup)) |
total_system_memory | 整个系统内存 |
total_system_memory_human | 以更直观的格式显示整个系统内存 |
used_memory_lua | Lua脚本存储占用的内存 |
used_memory_lua_human | 以更直观的格式显示Lua脚本存储占用的内存 |
maxmemory | Redis实例的最大内存配置 |
maxmemory_human | 以更直观的格式显示Redis实例的最大内存配置 |
maxmemory_policy | 当达到maxmemory时的淘汰策略 |
mem_fragmentation_ratio | 碎片率,used_memory_rss/ used_memory。ratio指数>1表明有内存碎片,越大表明越多,<1表明正在使用虚拟内存,虚拟内存其实就是硬盘,性能比内存低得多,这是应该增强机器的内存以提高性能。一般来说,mem_fragmentation_ratio的数值在1 ~ 1.5之间是比较健康的 |
mem_allocator | 内存分配器 |
active_defrag_running | 表示没有活动的defrag任务正在运行,1表示有活动的defrag任务正在运行(defrag:表示内存碎片整理)详解 |
lazyfree_pending_objects | 0表示不存在延迟释放的挂起对象 |
操作
# 连接reids
./redis-cli -p 6379 -a xxx
# 查看内存信息
info memory
指标:mem_fragmentation_ratio,它表示的就是 Redis 当前的内存碎片率。那么,这个碎片率是怎么计算的呢?其实,就是上面的命令中的两个指标 used_memory_rss 和 used_memory 相除的结果。
内存碎片率计算公式:
mem_fragmentation_ratio = used_memory_rss/ used_memory
used_memory_rss 是操作系统实际分配给 Redis 的物理内存空间,里面就包含了碎片;而 used_memory 是 Redis 为了保存数据实际申请使用的空间
多大的碎片率需要清理
重启redis,简单粗暴,见效快。但同时也带来一些问题:
如果使用重启大法,建议凌晨执行,先bgsave
,再重启。除了这个,还有其他方法吗?
使用这个方法,redis版本必须是4.0以上
直接通过 config set
命令将 activedefrag
配置项设置为 yes
即可。
config set activedefrag yes
这个命令只是启用了自动清理功能,具体什么时候清理需要通过下面两个参数控制:
# 内存碎片占用空间达到 500mb 的时候开始清理
config set active-defrag-ignore-bytes 500mb
# 内存碎片率大于 1.5 的时候开始清理
config set active-defrag-threshold-lower 50
通过 Redis 自动内存碎片清理机制可能会对 Redis 的性能产生影响,我们可以通过下面两个参数来减少对 Redis 性能的影响:
# 内存碎片清理所占用 CPU 时间的比例不低于 20%
config set active-defrag-cycle-min 20
# 内存碎片清理所占用 CPU 时间的比例不高于 50%
config set active-defrag-cycle-max 50
写作不易,刚好你看到,刚好对你有帮助,动动小手,点点赞,欢迎转发,有疑问的欢迎留言或者私信讨论,有问必回。