[转帖]玩转REDIS-删除了两百万KEY,为什么内存依旧未释放?

玩转,redis,除了,两百万,key,为什么,内存,依旧,释放 · 浏览次数 : 0

小编点评

**内存碎片** 内存碎片是指由于计划申请的空间比空闲的连续空间小,导致这部分小内存空间无法被使用,无法使用的内存空间则可称为内存碎片。 **Redis内存碎片的原因** * **内存分配器机制**:Redis使用内存分配器来管理内存,分配器根据内存需求动态调整分配器中使用的区域。当内存碎片导致分配器分配不到所需区域时,就会出现内存碎片。 * **数据写入**:当您向Redis写入数据时,该数据必须被存储到内存中。如果内存已满,则将无法存储数据,就会导致内存碎片。 * **数据读取**:当您从Redis中读取数据时,该数据也会被存储到内存中。如果内存已满,则将无法读取数据,也会导致内存碎片。 **内存碎片的影响** 内存碎片会导致以下问题: * **降低性能**:内存碎片会导致Redis搜索、排序等操作的速度下降。 * **内存消耗**:内存碎片会占用Redis的内存资源。 * **高内存使用率**:当内存碎片严重时,Redis的内存使用率可能会达到100%,导致系统性能下降。 **如何解决内存碎片** * **启用内存碎片监测**:您可以使用`info memory`命令查看mem_fragmentation_ratio,如果mem_fragmentation_ratio大于1.5,说明内存碎片严重,您可以考虑进行内存碎片清理。 * **调整内存分配器参数**:您可以使用`config set`命令调整内存分配器参数,例如`maxmemory`和`minmemory`。 * **定期清理内存碎片**:您可以使用`clearememory`命令定期清理内存碎片,但请注意,清理内存碎片可能会影响系统性能。

正文

https://www.freesion.com/article/87101375552/

 

  《玩转Redis》系列文章主要讲述Redis的基础及中高级应用。本文是《玩转Redis》系列第【12】篇,最新系列文章请前往公众号“zxiaofan”(点我点我)查看,或百度搜索“玩转Redis zxiaofan”即可。

往期精选:《玩转Redis-Redis中布隆过滤器的使用及原理》

本文关键字:玩转Redis、Redis内存碎片、Redis内存释放;

大纲

  • 背景
  • 如何查看Redis内存数据
  • 内存为何不释放
    • 什么是内存碎片
    • Redis的内存碎片是如何形成的
  • 如何释放内存
  • 生产环境整理内存碎片的注意事项

1、背景

  公司某业务使用的Redis集群是自建的,前段时间计划将自建Redis集群迁移到购买的阿里云集群。
  老集群共有 350W key,占用内存 8.8 G,DTS迁移前分析发现有近两百万的key无需迁移,于是提前删除了这两百万key。
  删除key后发现redis内存竟然几乎无变化,350W key删除了两百万,怎么也得释放几G内存吧。难道删除失败了?通过比对数据发现,计划被删除的数据确实已经删除了。
  为什么删除了两百万key,内存未释放呢?这个问题一直困扰着我,通过查阅资料终于弄明白了。

2、如何查看REDIS内存数据

2.1、INFO MEMORY

  进入Redis命令行界面后,使用 info memory 命令(集群使用 cluster info 命令) 即可查看当前Redis相关内存信息,部分信息展示如下:

127.0.0.1:6379> info memory
# Memory

# Redis 保存数据申请的内存空间
used_memory:9469412118
used_memory_human:8.82G

# 操作系统分配给 Redis 进程的内存空间
used_memory_rss:11351138316
used_memory_rss_human:10.57G

# Redis 进程在运行过程中占用的内存峰值
used_memory_peak:12618222522
used_memory_peak_human:11.75G

# 内存碎片率,used_memory_rss / used_memory
mem_fragmentation_ratio:1.20

# Redis 最大可用内存,0表示不限制
maxmemory:0
maxmemory_human:0B

# 内存分配器
mem_allocator:jemalloc-5.1.0

我们来解释下每个属性的含义:

used_memory:Redis 保存数据申请的内存空间,包含Redis进程及数据占用内存,单位 Byte;

used_memory_rss:操作系统分配给 Redis 进程的内存空间(包含内存碎片占用的空间),是从操作系统角度看的数据;此数据结果约等于top、ps命令看到的数据结果。

used_memory_peak:Redis 进程在运行过程中占用的内存峰值,used_memory_peak >= used_memory;

maxmemory:Redis 最大可用内存,0表示不限制。可以方便的实现对一台服务器部署多个Redis进程的内存控制;防止所用内存超过服务器物理内存;便于数据超出内存限制时执行LRU等删除策略。

XXX_human:表示以可读的方式返回XXX。

mem_fragmentation_ratio:内存碎片率,used_memory_rss / used_memory。大于1的部分为redis碎片占用的大小,建议值 大于 1 但小于 1.5,大于1.5说明碎片过多需要清理了。

  需要注意的是,通常情况下 used_memory_rss 是大于 used_memory 的;但也有例外,当used_memory_rss 小于 used_memory 时,说明 操作系统分配给Redis进程的数据,不足以满足实际存储数据的需求,此时Redis部分内存数据会转换到Swap中,随之引发的问题是,当Redis访问Swap中的数据时,性能会下降 。

2.2、MEMORY XXX 指令

2.2.1、MEMORY DOCTOR

  列出 Redis 服务器遇到的不同类型的内存相关问题,并提供相应的解决建议

127.0.0.1:6379> memory doctor
Hi Sam, I can't find any memory issue in your instance. I can only account for what occurs on this base.

2.2.2、MEMORY MALLOC-STATS

  提供内存分配情况的内部统计报表,(目前只支持jemalloc内存分配器)。通过该命令可以看出jemalloc对于所有对象进行分配时,各具体分区的内存详细状况。
  memory malloc-stats 命令返回信息较多,此处暂不做详细分析,感兴趣的同学可以自行执行命令分析。

2.2.3、MEMORY PURGE

  手动整理内存碎片,会阻塞主进程。

127.0.0.1:6379> memory purge
OK

2.2.4、MEMORY STATS

  以数组形式返回服务器的内存使用情况;

127.0.0.1:6379> memory stats
 1) "peak.allocated"
 2) (integer) 905331384
 3) "total.allocated"
 4) (integer) 905330152
 5) "startup.allocated"
 6) (integer) 791160
 7) "replication.backlog"
 8) (integer) 0
 9) "clients.slaves"
10) (integer) 0
11) "clients.normal"
12) (integer) 49694
13) "aof.buffer"
14) (integer) 0
15) "lua.caches"
16) (integer) 0
17) "db.0"
18) 1) "overhead.hashtable.main"
    2) (integer) 7888
    3) "overhead.hashtable.expires"
    4) (integer) 32
19) "db.1"
20) 1) "overhead.hashtable.main"
    2) (integer) 304
    3) "overhead.hashtable.expires"
    4) (integer) 32
21) "overhead.total"
22) (integer) 849110
23) "keys.count"
24) (integer) 152
25) "keys.bytes-per-key"
26) (integer) 5950914
27) "dataset.bytes"
28) (integer) 904481042
29) "dataset.percentage"
30) "99.99359130859375"
31) "peak.percentage"
32) "99.999870300292969"
33) "allocator.allocated"
34) (integer) 905598528
35) "allocator.active"
36) (integer) 905961472
37) "allocator.resident"
38) (integer) 910348288
39) "allocator-fragmentation.ratio"
40) "1.0004007816314697"
41) "allocator-fragmentation.bytes"
42) (integer) 362944
43) "allocator-rss.ratio"
44) "1.0048421621322632"
45) "allocator-rss.bytes"
46) (integer) 4386816
47) "rss-overhead.ratio"
48) "0.0086568007245659828"
49) "rss-overhead.bytes"
50) (integer) -902467584
51) "fragmentation"
52) "0.0087051792070269585"
53) "fragmentation.bytes"
54) (integer) -897408432

2.2.5、MEMORY USAGE

  返回一个key和它值在内存中占用的字节数(包含redis管理该key所占用的内存);

127.0.0.1:6379> memory usage key1
(integer) 46

2.2.6、MEMORY HELP

  memory相关命令的帮助信息;

127.0.0.1:6379> memory  help
1) MEMORY <subcommand> arg arg ... arg. Subcommands are:
2) DOCTOR - Return memory problems reports.
3) MALLOC-STATS -- Return internal statistics report from the memory allocator.
4) PURGE -- Attempt to purge dirty pages for reclamation by the allocator.
5) STATS -- Return information about the memory usage of the server.
6) USAGE <key> [SAMPLES <count>] -- Return memory in bytes used by <key> and its value. Nested values are sampled up to <count> times (default: 5).

 

3、内存为何不释放

  Redis有自己的内存分配器,当数据删除后,释放的内存空间由Redis自己的内存分配器管理,并没有立即将内存返回给操作系统,所以对于操作系统而言,仍然认为Redis占用了内存。

  这样的好处是,减少Redis向系统申请内存分配的次数,提升Redis自身性能。

3.1、什么是内存碎片

  不少同学应该听说过磁盘碎片,使用过Smart Defrag等软件清理过磁盘碎片,清理磁盘碎片能够优化文件系统,将零散的磁盘空间移动合并,将频繁使用的文件和目录放置到磁盘的速度最快的区域,使计算机能以最高速度稳定运行。

  内存碎片是由于计划申请的空间比空闲的连续空间小,导致这部分小内存空间无法被使用,无法被使用的内存空间则可称为内存碎片。

内存碎片示意图

  如图,9字节的内存空间示意图,序号为1、2、3、5、6、9的内存空间已使用,如果现在计划申请3字节的连续内存空间,按照现有的内存使用情况是无法申请的,此时序号4、7、8就是“内存碎片”了。

3.2、REDIS的内存碎片是如何形成的

Redis产生内存碎片主要由以下2点原因导致;

  • 内存分配器机制;
  • Redis数据的修改和删除引发空间的扩容和释放;

3.2.1、内存分配器机制

  Redis有几种内存分配器 jemalloc、libc、tcmalloc,默认使用 jemalloc。
  jemalloc 内存分配方式为 按照一系列固定大小分配内存空间,jemalloc 按照申请的内存大小分配最接近的内存空间;
  比如申请220字节,jemalloc 会分配256字节,如果还要继续写入20字节,Redis则不会继续向系统申请内存空间,因为先前申请的256字节还剩余36字节可用;但如果此时需要继续写入60字节,则已分配空间不够用了,需要再次向系统申请分配内存空间。

默认64位系统jemalloc的划分方式如下:
Small: [8], [16, 32, 48, …, 128], [192, 256, 320, …, 512], [768, 1024, 1280, …, 3840]
Large: [4 KiB, 8 KiB, 12 KiB, …, 4072 KiB]
Huge: [4 MiB, 8 MiB, 12 MiB, …]

3.2.2、REDIS数据的修改和删除引发空间的扩容和释放

  如下图所示,key1扩容时需新增2字节,为保证内存空间连续性,key2发生迁移;当key3释放空间后,序号为7、8、14、15的空间均未使用。
  如果此时有key希望申请3字节的空间,虽然总共剩余了4字节,但是没有连续的3字节空间,所以无法直接使用。

  了解了原理后,如果我们想测试内存碎片清理,则可以插入大量key,再删除大量key(或者插入key时设置过期时间),以此来模拟高内存碎片率场景。

内存空间的扩容和释放

4、如何释放内存

4.1、重启REDIS释放内存

  这应该是最直接有效的方法。但是生产环境不是你想重启就能重启的。因为重启Redis需要考虑很多问题,比如:

  • 重启时加载恢复数据需要时间,在此期间Redis将不可用;
  • 确保所有的配置项变更都更新到redis.conf,否则重启后在线修改的配置将还原;

4.2、使用备用REDIS实例同步迁移

  内存碎片的产生很大程度上是因为数据的修改和删除,所以计划清理 实例A的内存碎片时,我们可以引入实例B,将实例A的数据全量同步到实例B中,再使用实例B替换原有的实例A对外提供服务。

  此方法思路虽然是通的,但操作流程复杂且风险较高,生产环境几乎不会采用。但如果本就计划迁移实例,那就刚好可以采用此种思路。

4.3、MEMORY PURGE手动碎片整理

  手动整理内存碎片,会阻塞主进程,生产环境慎用。

  memory purge 和 activedefrag回收的并不是同一块区域的内存,它简单粗暴的尝试清除脏页以便内存分配器回收。可以根据实际情况和activedefrag配合使用,memory purge在极端情况下效果较好,activedefrag则更彻底。

4.4、开启ACTIVEDEFRAG自动碎片整理

  在Redis 4.0 版本后新增配置项activedefrag(active:主动的,defrag:整理碎片),activedefrag默认关闭,计划清理碎片时需手动开启,命令如下:

127.0.0.1:6379> config set activedefrag yes

 

  让我们看看相关的配置文件:

# 以下内容节选至 redis.conf

########################### ACTIVE DEFRAGMENTATION #######################

# 3. Once you experience fragmentation, you can enable this feature when
#    needed with the command "CONFIG SET activedefrag yes".
#

# 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 5

# Maximal effort for defrag in CPU percentage
# active-defrag-cycle-max 75

# Maximum number of set/hash/zset/list fields that will be processed from
# the main dictionary scan
# active-defrag-max-scan-fields 1000

内存碎片整理开关(需同时满足才执行):

  • activedefrag:内存碎片整理总开关,开启后才有可能执行碎片整理;
  • active-defrag-ignore-bytes:内存碎片的字节数达到此阀值(默认100MB)时,允许整理;
  • active-defrag-threshold-lower:内存碎片空间占操作系统分配给 Redis 的总空间比例达到此阀值(默认10%)时,允许整理;

此外,还有几个参数用于控制内存碎片整理的力度:

  • active-defrag-cycle-min:清理内存碎片占用 CPU 时间的比例不低于此阀值(默认5%),保证清理能正常开展;
  • active-defrag-cycle-max:清理内存碎片占用CPU 时间的比例不高于此阀值(默认75%),一旦超过则停止清理,从而避免在清理时,大量的内存拷贝阻塞 Redis,导致其他请求延迟。

内存碎片整理其他参数:

  • active-defrag-threshold-upper:内存碎片空间占操作系统分配给 Redis 的总空间比例达到此阀值(默认100%)时,则尽最大努力整理;
  • active-defrag-max-scan-fields:碎片整理 扫描set/hash/zset/list时,仅当 set/hash/zset/list 的长度小于此阀值时,才会将此key加入碎片整理;

5、生产环境整理内存碎片的注意事项

5.1、清理内存碎片的时机

  通过“info memory”命令查看mem_fragmentation_ratio(内存碎片率),当mem_fragmentation_ratio > 1.5 时,建议开始清理内存碎片。当然,也可以通过分析调整activedefrag的参数配置从而达到自动清理效果。

5.2、清理内存碎片前你必须了解的事

memory purge:手动整理内存碎片,会阻塞主进程,生产环境慎用,清理效果和activedefrag并不相同。

activedefrag:自动整理内存碎片,其原理是通过scan迭代整个Redis数据,通过一系列的内存复制、转移操作完成内存碎片整理,由于此操作使用的是主线程,故会影响Redis对其他请求的响应。

  在Redis日志中,可以查看activedefrag耗时及资源占用记录:Active defrag done in 79214ms 表示 耗时,但此耗时并不是阻塞了主线程的时间,而是从内存碎片整理的第一次scan到最后一次scan的时间差,在此期间,主线程是可以处理其他请求的。

12:M 21 May 12:31:11.210 - Starting active defrag, frag=12%, frag_bytes=381401201, cpu=75%
12:M 21 May 12:32:30.424 - Active defrag done in 79214ms, reallocated=50, frag=12%, frag_bytes=380061210

【玩转Redis系列文章 近期精选 @zxiaofan】

《玩转Redis-Redis中布隆过滤器的使用及原理》

《玩转Redis-HyperLogLog原理探索》

《玩转Redis-HyperLogLog统计微博日活月活》

《玩转Redis-京东签到领京豆如何实现》

《玩转Redis-老板带你深入理解分布式锁》

与[转帖]玩转REDIS-删除了两百万KEY,为什么内存依旧未释放?相似的内容:

[转帖]玩转REDIS-删除了两百万KEY,为什么内存依旧未释放?

https://www.freesion.com/article/87101375552/ 《玩转Redis》系列文章主要讲述Redis的基础及中高级应用。本文是《玩转Redis》系列第【12】篇,最新系列文章请前往公众号“zxiaofan”(点我点我)查看,或百度搜索“玩转Redis zxiaof

[转帖]一文带你玩转 Redis 的 RESP 协议 !

https://zhuanlan.zhihu.com/p/384251739 RESP 是 Redis 客户端与 Redis 服务器相互通信时使用的一个协议, 全称 REdis Serialization Protocol ,即 redis 串行协议,通俗易懂,也表明了 redis 的特点,串行化(

[转帖]看看 Jmeter 是如何玩转 redis 数据库的

柠檬小欧 2021-08-31 20:06420 Jmeter 作为当前非常受欢迎的接口测试和性能测试的工具,在企业中得到非常广泛的使用,而 Redis 作为缓存数据库,也在企业中得到普遍使用,那如何使用 jmeter 来测试 Redis 数据库呢?今天我们就来讲一讲怎么使用 jmeter 来调用

[转帖]fastJson与一起堆内存溢出'血案'

https://www.jianshu.com/p/876d443c2162 现象 QA同学反映登录不上服务器 排查问题1--日志级别 查看log,发现玩家登录的时候抛出了一个java.lang.OutOfMemoryError 大概代码是向Redis序列化一个PlayerMirror镜像数据,但是

[转帖]玩转zabbix之超详细的二进制安装

https://zhuanlan.zhihu.com/p/212281069 #初始配置 #centos7添加阿里云镜像 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo #安

[转帖]玩转 Ceph 的正确姿势

玩转 Ceph 的正确姿势 https://www.cnblogs.com/me115/p/6366374.html Ceph 客户端 Ceph 服务端 总结 参考 玩转 Ceph 的正确姿势本文先介绍 Ceph, 然后会聊到一些正确使用 Ceph 的姿势;在集群规模小的时候,Ceph 怎么玩都没问

[转帖]玩转 Ceph 的正确姿势

https://www.cnblogs.com/me115/p/6366374.html 内容目录: Ceph 客户端 Ceph 服务端 总结 参考 玩转 Ceph 的正确姿势本文先介绍 Ceph, 然后会聊到一些正确使用 Ceph 的姿势;在集群规模小的时候,Ceph 怎么玩都没问题;但集群大了(

[转帖]玩火的容器内存控制 CGroup - 容器基础拾遗 Part 1

https://www.modb.pro/db/555818 引 我们在谈容器内存时,到底在谈什么? CGroup 内存说明 强制回收内存 memory.force_empty 基于内存阈值水位的通知 不要 OOM Kill,只是暂停 memory.stat memory.usage_in_byte

[转帖]正则表达式边玩边学

最近在学习极客时间的《正则表达式入门课》,感觉很适合入门玩,所以简单作一些笔记方便查找参考。 正则,就是正则表达式,英文是 Regular Expression,简称 RE。顾名思义,正则其实就是一种描述文本内容组成规律的表示方式。在编程语言中,正则常常用来简化文本处理的逻辑。在 Linux 命令中

[转帖]关于一致性哈希算法在游戏服务器端的思考

https://www.jianshu.com/p/b8ae27cf22a9 突然想明白 其实网易的将军令 就是一个一致性哈希的玩法 关于一致性哈希算法在游戏服务器端的思考 需求分析 后端有很多逻辑node节点(not-section binded),节点启动后注册到注册中心 node本身有状态,有