Redis:删除数据后,内存占用率还是很高

背景

前面写的文章redis数据迁移,在迁移过程中,明明做了数据删除,删除的数据量已是好几个G,但我使用 top 命令查看redis进程时,发现 Redis 还是占用了很多内存,有点懵,怀疑是不是删除失败了。连上redis,查看删除的key是否存在,一查的确删除的数据已经不在redis里面的,那是为什么呢?

查询了下官方文档,当数据删除后,Redis 释放的内存空间会由内存分配器管理,并不会立即返回给操作系统。所以,操作系统仍然会记录着给 Redis 分配了大量内存。

Redis 释放的内存空间可能并不是连续的,就会产生内存碎片,那么,这些不连续的内存空间很有可能处于一种闲置的状态。这就会导致一个问题:虽然有空闲空间,Redis 却无法用来保存数据,不仅会减少 Redis 能够实际保存的数据量,还会降低 Redis 运行机器的成本回报率。

内存碎片

通常情况下,内存空间闲置,往往是因为操作系统发生了较为严重的内存碎片。那么,什么是内存碎片呢?

举例:

我向操作系统申请内存,操作系统为我分配了 32 字节的连续内存空间,我的数据只需要24字节的内存空间就够了,还剩8字节的内存空间。后续我再存储24字节的数据,空间不够了。再向系统申请内存空间,系统再次分配给我32字节的内存空间,再次使用了24字节的连续内存空间,那剩余的两个 8字节的内存空间,如果后续没有被使用,就可以被称为内存碎片

Redis内存碎片如何产生的

内存分配器的分配策略

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 字节已经够用了,这就避免了一次分配操作。

频繁修改 Redis 中的数据也会产生内存碎片

修改数据

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表示不存在延迟释放的挂起对象

操作

js 复制代码
# 连接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 为了保存数据实际申请使用的空间

多大的碎片率需要清理

  • mem_fragmentation_ratio 大于 1 但小于 1.5。这种情况是合理的。这是因为,刚才我介绍的那些因素是难以避免的。毕竟,内因的内存分配器是一定要使用的,分配策略都是通用的,不会轻易修改;而外因由 Redis 负载决定,也无法限制。所以,存在内存碎片也是正常的。
  • mem_fragmentation_ratio 大于 1.5 。这表明内存碎片率已经超过了 50%。一般情况下,这个时候,我们就需要采取一些措施来降低内存碎片率了。意味着你使用 Redis 存储实际大小 2G 的数据需要使用大于 3G 的内存。

如何清理内存碎片

重启redis

重启redis,简单粗暴,见效快。但同时也带来一些问题:

  1. 如果 Redis 中的数据没有持久化,那么,数据就会丢失;
  2. 如果Redis 数据持久化了,我们还需要通过 AOF 或 RDB 进行恢复,恢复时长取决于 AOF 或 RDB 的大小,如果只有一个 Redis 实例,恢复阶段无法提供服务。

如果使用重启大法,建议凌晨执行,先bgsave,再重启。除了这个,还有其他方法吗?

Redis 自身内存碎片自动清理

使用这个方法,redis版本必须是4.0以上

直接通过 config set 命令将 activedefrag 配置项设置为 yes 即可。

js 复制代码
config set activedefrag yes

这个命令只是启用了自动清理功能,具体什么时候清理需要通过下面两个参数控制:

js 复制代码
# 内存碎片占用空间达到 500mb 的时候开始清理
config set active-defrag-ignore-bytes 500mb
# 内存碎片率大于 1.5 的时候开始清理
config set active-defrag-threshold-lower 50

通过 Redis 自动内存碎片清理机制可能会对 Redis 的性能产生影响,我们可以通过下面两个参数来减少对 Redis 性能的影响:

js 复制代码
# 内存碎片清理所占用 CPU 时间的比例不低于 20%
config set active-defrag-cycle-min 20
# 内存碎片清理所占用 CPU 时间的比例不高于 50%
config set active-defrag-cycle-max 50

写作不易,刚好你看到,刚好对你有帮助,动动小手,点点赞,欢迎转发,有疑问的欢迎留言或者私信讨论,有问必回。

相关推荐
程序员鱼皮14 分钟前
我代表编程导航,向大家道歉!
前端·后端·程序员
zjjuejin24 分钟前
Maven 生命周期与插件机制
后端·maven
阿杆40 分钟前
为什么我建议你把自建 Redis 迁移到云上进行托管
redis·后端
间彧1 小时前
什么是Redis分布式锁,有何使用场景
redis
Java水解1 小时前
go语言教程(全网最全,持续更新补全)
后端·go
bobz9651 小时前
QEMU 使用 DPDK 时候在 libvirt xml 中设置 sock 的目的
后端
thinktik1 小时前
AWS EKS 计算资源自动扩缩之按需申请Fargate[AWS 中国宁夏区]
后端·aws
thinktik2 小时前
AWS EKS 实现底层EC2计算资源的自动扩缩[AWS 中国宁夏区]
后端·aws
uhakadotcom2 小时前
什么是OpenTelemetry?
后端·面试·github
知其然亦知其所以然2 小时前
MySQL 社招必考题:如何优化特定类型的查询语句?
后端·mysql·面试