关于java:为什么Redis的数据被删除内存占用还这么大

51次阅读

共计 3618 个字符,预计需要花费 10 分钟才能阅读完成。


操作系统调配给 Redis 的内存有 6GB,通过指标 used_memory_human 发现存储数据只应用了 4GB,为何会这样?为何无奈保留数据?

通过 CONFIG SET maxmemory 100mb 或者在 redis.conf 配置文件设置 maxmemory 100mb Redis 内存占用限度。当达到内存最大值,会触发内存淘汰策略删除数据。

除此之外,当 key 达到过期工夫,Redis 会有以下两种删除过期数据的策略:

  • 后盾定时工作选取局部数据删除;
  • 惰性删除。


假如 Redis 实例保留了 5GB 的数据,当初删除了 2GB 数据,Redis 过程占用的内存肯定会升高么?(也叫做 RSS,过程耗费内存页数)。

答案是:可能仍然占用了大概 5GB 的内存,即便 Redis 的数据只占用了 3GB 左右。

大家肯定要设置 maxmemory,否则 Redis 会持续为新写入的数据分配内存,无奈调配就会导致应用程序报错,当然不会导致宕机。

开释的内存去哪了


明明删除了数据,应用 top 命令查看,为何还是占用了那么多内存?

内存都去哪了?应用 info memory 命令获取 Redis 内存相干指标,我列举了几个重要的数据:

127.0.0.1:6379> info memory
# Memory
used_memory:1132832  // Redis 存储数据占用的内存量
used_memory_human:1.08M  // 人类可读模式返回内存总量
used_memory_rss:2977792  // 操作系统角度,过程占用的物理总内存
used_memory_rss_human:2.84M // used_memory_rss 可读性模式展现
used_memory_peak:1183808 // 内存应用的最大值,示意 used_memory 的峰值

used_memory_peak_human:1.13M  // 以可读的格局返回 used_memory_peak 的值
used_memory_lua:37888   // Lua 引擎所耗费的内存大小。used_memory_lua_human:37.00K
maxmemory:2147483648    // 能应用的最大内存值,字节为单位。maxmemory_human:2.00G  // 可读模式
maxmemory_policy:noeviction // 内存淘汰策略

// used_memory_rss / used_memory 的比值,代表内存碎片率
mem_fragmentation_ratio:2.79

Redis 过程内存耗费次要由以下局部组成:

  • Redis 本身启动所占用的内存;
  • 存储对象数据内存;
  • 缓冲区内存:次要由 client-output-buffer-limit 客户端输入缓冲区、复制积压缓冲区、AOF 缓冲区。
  • 内存碎片。

Redis 本身空过程占用的内存很小能够忽略不计,对象内存是占比最大的一块,外面存储着所有的数据。

缓冲区内存在大流量场景容易失控,造成 Redis 内存不稳固,须要重点关注。

内存碎片过大会导致明明有空间可用,然而却无奈存储数据。

碎片 = used_memory_rss 理论应用的物理内存(RSS 值)除以 used_memory 理论存储数据内存。

什么是内存碎片

内存碎片会造成明明有内存空间闲暇,可是却无奈存储数据。举个例子,你跟丑陋小姐姐去电影院看电影,必定想连在一块。

假如当初有 8 个座位,曾经卖出了 4 张票,还有 4 张能够买。可是好巧不巧,买票的人很奇葩,别离距离一个座位买票。

即便还有 4 个座位闲暇,可是你却买不到两个座位连在一块的票,薄礼蟹!

内存碎片造成起因

内存碎片是什么起因导致呢?

次要有两个起因:

  • 内存分配器的调配策略。
  • 键值对的大小不一样和删改操作:Redis 频繁做更新操作、大量过期数据删除,开释的空间(不够间断)无奈失去复用,导致碎片率回升。

接下来我别离探讨理论产生的起因……

内存分配器的调配策略

Redis 默认的内存分配器采纳 jemalloc,可选的分配器还有:glibc、tcmalloc。

内存分配器并不能做到按需分配,而是采纳固定范畴的内存块进行调配。

例如 8 字节、16 字节…..,2 KB,4KB,当申请内存最近接某个固定值的时候,jemalloc 会给它调配最靠近固定值大小的空间。

这样就会呈现内存碎片,比方程序只须要 1.5 KB,内存分配器会调配 2KB 空间,那么这 0.5KB 就是碎片。

这么做的目标是缩小内存调配次数,比方申请 22 字节的空间保留数据,jemalloc 就会调配 32 字节,如果后边还要写入 10 字节,就不须要再向操作系统申请空间了,能够应用之前申请的 32 字节。

删除 key 的时候,Redis 并不会立马把内存归还给操作系统,呈现这个状况是因为底层内存分配器治理导致,比方大多数曾经删除的 key 仍然与其余无效的 key 调配在同一个内存页中。

另外,分配器为了复用闲暇的内存块,原有 5GB 的数据中删除了 2 GB 后,当再次增加数据到实例中,Redis 的 RSS 会保持稳定,不会增长太多。

因为 内存分配器基本上复用了之前删除释放出来的 2GB 内存。

键值对大小不一样和删改操作

因为内存分配器是依照固定大小分配内存,所以通常调配的内存空间比理论数据占用的大小多一些,会造成碎片,升高内存的存储效率。

另外,键值对的频繁批改和删除,导致内存空间的扩容和开释,比方本来占用 32 字节的字符串,当初批改为占用 20 字节的字符串,那么开释出的 12 字节就是闲暇空间。

如果下一个数据存储申请须要申请 13 字节的字符串,那么刚刚开释的 12 字节空间无奈应用,导致碎片。

碎片最大的问题:空间总量足够大,然而这些内存不是间断的,可能大抵无奈存储数据。

内存碎片解决之道


那该如何解决呢?

首先要确定是否产生了内存碎片,重点关注后面 INFO memory 命令提醒的 mem_fragmentation_ratio 指标,示意内存碎片率:

mem_fragmentation_ratio = used_memory_rss/ used_memory

如果 1 < 碎片率 < 1.5,能够认为是正当的,而大于 1.5 阐明碎片曾经超过 50%,咱们须要采取一些伎俩解决碎片率过大的问题。

重启大法

最简略粗犷的形式就是重启,如果没有开启长久化,数据会失落。

开启长久化的话,须要应用 RDB 或者 AOF 复原数据,如果只有一个实例,数据大的话会导致复原阶段长时间无奈提供服务,高可用大打折扣。

主动清理内存碎片

既然你都叫我靓仔了,就倾囊相助通知你终极杀招:Redis 4.0 版本后,本身提供了一种内存碎片清理机制。


怎么清理呢?

很简略,还是下面的例子,想要买两张连在一块的电影票。与与他人沟通调换下地位,就实现了。

对于 Redis 来说,当一块间断的内存空间被划分为好几块不间断的空间的时候,操作系统先把数据以顺次移动拼接在一块,并开释原来数据占据的空间,造成一块间断闲暇内存空间。。

如下图所示:

主动清理内存碎片的代价

主动清理虽好,可不要肆意妄为,操作系统把数据挪动到新地位,再把原有空间开释是须要耗费资源的。

Redis 操作数据的指令是单线程,所以在数据复制挪动的时候,只能期待清理碎片实现能力解决申请,造成性能损耗。


如何防止清理碎片对性能的影响又能实现主动清理呢?

好问题,通过以下两个参数来管制内存碎片清理和完结机会,防止占用 CPU 过多,缩小清理碎片对 Redis 解决申请的性能影响。

开启主动内存碎片清理

CONFIG SET activedefrag yes

这只是开启主动清理,何时清理要同时满足以下两个条件才会触发清理操作。

清理的条件

active-defrag-ignore-bytes 200mb:内存碎片占用的内存达到 200MB,开始清理;

active-defrag-threshold-lower 20:内存碎片的空间占比超过零碎调配给 Redis 空间的 20%,开始清理。

防止对性能造成影响

清理工夫有了,还须要管制清理对性能的影响。由一项两个设置先调配清理碎片占用的 CPU 资源,保障既能失常清理碎片,又能防止对 Redis 解决申请的性能影响。

active-defrag-cycle-min 20:主动清理过程中,占用 CPU 工夫的比例不低于 20%,从而保障能失常开展清理工作。

active-defrag-cycle-max 50:主动清理过程占用的 CPU 工夫比例不能高于 50%,超过的话就立即进行清理,防止对 Redis 的阻塞,造成高提早。

总结

如果你发现明明 Redis 存储数据的内存占用远小于操作系统调配给 Redis 的内存,而又无奈保留数据,那可能呈现大量内存碎片了。

通过 info memory 命令,看下内存碎片 mem_fragmentation_ratio 指标是否失常。

那么咱们就开启主动清理并正当设置清理机会和 CPU 资源占用,该机制波及到内存拷贝,会对 Redis 性能造成潜在危险。

如果遇到 Redis 性能变慢,排查下是否因为清理碎片导致,如果是,那就调小 active-defrag-cycle-max 的值。

正文完
 0