Redis内存碎片原理深入分析

目录
  • 前言
  • 释放的内存去了哪里?
  • 什么是内存碎片?
  • 什么导致内存碎片?
  • 如何解决?
  • 总结

前言

我们先来看一个问题, 假设Redis实例保存了5GB的数据,现在删除了2GB的数据,那么Redis进程占用的内存会不会减少呢?

答案是:它可能仍然占用大约5GB内存,即使Redis数据只占用大约3GB。

如果maxmemory不设置该参数,Redis不会触发内存淘汰策略删除数据。

Redis会继续为新写入的数据分配内存。分配失败会导致应用程序报错,当然不会导致宕机。

注:设置maxmemory参数,执行命令CONFIG SET maxmemory 100mb,或在redis.conf 配置文件中设置maxmemory 100mb。

使用top命令查看数据是否已经删除,为什么它仍然占用这么多内存?

释放的内存去了哪里?

当我们使用top命令查看系统使用情况时,会发现内存依然很高,Redis并没有真正释放内存。那么内存都去哪儿了?这时候我们就需要使用info memory命令获取Redis内存相关的指标。

127.0.0.1:6379> info memory
# Memory
used_memory:1132832           // Redis Amount of memory used to store data
used_memory_human:1.08M       // Returns the total amount of memory in human readable form
used_memory_rss:2977792       // From the perspective of the operating system, the total physical memory occupied by the process
used_memory_rss_human:2.84M   // used_memory_rss Readability mode display
used_memory_peak:1183808      // The maximum value of memory used, representing the peak value of used_memory
used_memory_peak_human:1.13M  // Returns the value of used_memory_peak in a human readable format
used_memory_lua:37888         // Lua The amount of memory consumed by the engine。
used_memory_lua_human:37.00K
maxmemory:2147483648          // The maximum memory value that can be used, in bytes.
maxmemory_human:2.00G         // readable form
maxmemory_policy:noeviction   // Memory Retirement Policy
mem_fragmentation_ratio:2.79  // The ratio of used_memory_rss & used_memory represents the memory fragmentation rate

Redis进程内存消耗主要由以下几部分组成:

  • 内存被Redis自己启动占用
  • 存储对象数据内存
  • 缓冲区内存:主要由client-output-buffer-limit客户端输出缓冲区、copy backlog缓冲区、AOF缓冲区组成
  • 内存碎片

Redis自身的空进程占用的内存很小,可以忽略不计,而对象内存是最大的,里面存放了所有的数据。

需要注意, 如果缓冲区有大流量的场景很容易失控,导致Redis内存不稳定。

内存碎片过多导致有可用空间不足,无法存储数据。内存碎片Fragmentation = used_memory_rss 实际使用的物理内存(RSS 值)除以 used_memory 实际存储的数据内存。

什么是内存碎片?

内存碎片会导致内存空间空闲,但无法存储数据。比如你和女朋友去电影院看电影,你们肯定是要在一起的。

假设现在有 8 个座位,已售出 4 张票,还有 4 张可供购买。不过巧合的是,买票的人很奇怪,都是隔一个座位买票。即使还有4个座位,也不能买顺序连接两个座位的票。

什么导致内存碎片?

主要有两个原因:

  • 内存分配器的分配策略
  • 键值对的大小不同,删除操作

下面我们就实际发生的原因进行探讨。

1. 内存分配器的分配策略

Redis 默认的内存分配器使用jemalloc,可选的分配器有:glibctcmalloc

内存分配器不能按需分配,而是使用固定范围的内存块进行分配。

比如8字节、16字节……、2KB、4KB,当申请内存最接近固定值时,jemalloc会分配最接近固定值的空间。这样就会出现内存碎片。

比如程序只需要1.5KB,而内存分配器会分配2KB的空间,那么这0.5KB就是碎片。这样做的目的是减少内存分配的次数。比如你申请22个字节的空间来存放数据,jemalloc就会分配32个字节。如果后面需要写入10个字节,则不需要向操作系统申请空间。您可以使用之前请求的 32 个字节。

当一个键被删除时,Redis 不会立即将内存归还给操作系统。发生这种情况是因为底层内存分配器的管理。例如,大多数已删除的键仍与其他有效键分配在同一内存页中。

此外,为了重用空闲内存块,分配器删除了原始 5 GB 数据中的 2 GB。再次向实例添加数据时,Redis的RSS会保持稳定,不会增加太多。因为内存分配器基本上重新使用了之前删除释放的2GB内存。

2.键值对大小不同,删除操作

由于内存分配器是按照固定的大小分配内存,因此分配的内存空间通常会大于实际数据占用的大小,这会造成碎片,降低内存的存储效率。

另外,键值对的频繁修改和删除导致内存空间的扩大和释放。例如,如果原来占用32个字节的字符串现在修改为占用20个字节的字符串,那么释放的12个字节就是空闲空间。

如果下一次数据存储请求需要申请一个13字节的字符串,刚刚释放的12字节空间就不能使用,造成碎片。

分片最大的问题:空间总量足够大,但是这些内存并不连续,可能存不下数据。

mem_fragmentation_ratio = used_memory_rss/ used_memory

如何解决?

首先要判断是否发生了内存碎片,重点看info memory命令执行后的mem_fragmentation_ratio指标,表示内存碎片率。

如果1 < mem_fragmentation_ratio < 1.5,可以认为是合理的,如果大于1.5,说明碎片已经超过了50%,我们需要采取一些措施来解决碎片过多的问题。

1. 重启

最简单的方法是重新启动。如果未启用持久性,数据将丢失。

如果开启持久化,需要使用RDB或者AOF来恢复数据。如果只有一个实例,数据量大会导致恢复阶段长时间无法提供服务,高可用性会大大降低。

2.自动清理内存碎片

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的值。

以上就是Redis内存碎片原理深入分析的详细内容,更多关于Redis内存碎片的资料请关注我们其它相关文章!

(0)

相关推荐

  • Redis集群新增、删除节点以及动态增加内存的方法

    目录 一.新增服务节点到集群中 1.创建配置文件 2.启动新的端口 3.将新增的两个端口增加到现有集群中 4.设置从节点 5.设置主节点master 二.删除节点 1.删除从节点 2.删除主节点 三.动态扩容内存 1.动态将7002端口内存从5G提升至10G 一.新增服务节点到集群中 1.创建配置文件 在主机127.0.0.5上创建新端口的配置文件,如之前有端口直接复制之前的配置文件即可.复制完然后修改下配置文件里的端口.内存大小.pid的路径等. cp redis7001.conf redis

  • Redis 的内存淘汰策略和过期删除策略的区别

    目录 前言 过期删除策略 如何设置过期时间? 如何判定 key 已过期了? 过期删除策略有哪些? Redis 过期删除策略是什么? 内存淘汰策略 如何设置 Redis 最大运行内存? Redis 内存淘汰策略有哪些? LRU 算法和 LFU 算法有什么区别? 总结 前言 Redis 是可以对 key 设置过期时间的,因此需要有相应的机制将已过期的键值对删除,而做这个工作的就是过期键值删除策略. Redis 的「内存淘汰策略」和「过期删除策略」,很多小伙伴容易混淆,这两个机制虽然都是做删除的操作,

  • sprintboot使用spring-security包,缓存内存与redis共存方式

    目录 项目修改需求描述 代码 使用到的依赖包 代码处理 异常问题解决 项目修改需求描述 项目需要使用分布式缓存机制,但是使用@Cacheable原始仅配置了内存版的,故此次需要改成redis用以支持多应用模式的. 项目中如果直接改成redis的,存在一个问题.如果内存对象同一类,比如都是String的list对象,存的key值又都是"code",会把缓存给冲掉,所以需要对redis的做后缀处理.又因为只有一个redis服务器,所以需要对缓存做项目的前缀处理. 还希望能支持历史项目,做到

  • 设置Redis最大占用内存的实现

    目录 打开redis配置文件 我们可以在CentOS下输入命令:find / -name redis查找redis目录: Redis使用超过设置的最大值 使用info命令查看Redis内存使用情况 Redis需要设置最大占用内存吗?如果Redis内存使用超出了设置的最大值会怎样? 打开redis配置文件 找到如下段落,设置maxmemory参数,maxmemory是bytes字节类型,注意转换.修改如下所示: # In short... if you have slaves attached i

  • Redis内存回收策略

    目录 概述 maxmemory-policy 参数 主动清理策略 策略选择 maxmemory-sample 概述 Redis也会因为内存不足而产生错误 , 也可能因为回收过久而导致系统长期的停顿,因此掌握执行回收策略十分有必要.在 Redis 的配置文件中,当 Redis 的内存达到规定的最大值时,允许配置 6 种策略中的一种进行淘汰键值,并且将一些键值对进行回收. maxmemory-policy 参数 # Set a memory usage limit to the specified

  • Redis内存碎片处理实例详解

    目录 Redis内存碎片处理 模拟bigkey删除 什么是内存碎片 内存碎片如何产生 内存分配器 键值大小不同 键值的操作 碎片信息如何查看 碎片率如何降低 总结 Redis内存碎片处理 不知道我们在执行删除操作时有没有注意过这样一个现象,删除一些bigkey后内存分配器分配的容量并没有减少,实际容量减少了,这是为什么呢?演示如下 模拟bigkey删除 创建生成bigkey的脚本文件createdata.sh #!/bin/bash cd /opt/redis/redis-6.0.6/bin/

  • Redis内存碎片原理深入分析

    目录 前言 释放的内存去了哪里? 什么是内存碎片? 什么导致内存碎片? 如何解决? 总结 前言 我们先来看一个问题, 假设Redis实例保存了5GB的数据,现在删除了2GB的数据,那么Redis进程占用的内存会不会减少呢? 答案是:它可能仍然占用大约5GB内存,即使Redis数据只占用大约3GB. 如果maxmemory不设置该参数,Redis不会触发内存淘汰策略删除数据. Redis会继续为新写入的数据分配内存.分配失败会导致应用程序报错,当然不会导致宕机. 注:设置maxmemory参数,执

  • Redis核心原理与实践之字符串实现原理

    本文分析Redis字符串的实现原理,内容摘自新书<Redis核心原理与实践>.这本书深入地分析了Redis常用特性的内部机制与实现方式,内容源自对Redis源码的分析,并从中总结出设计思路.实现原理.通过阅读本书,读者可以快速.轻松地了解Redis的内部运行机制. Redis是一个键值对数据库(key-value DB),下面是一个简单的Redis的命令: > SET msg "hello wolrd" 该命令将键"msg".值"hell

  • SQL查询的底层运行原理深入分析

    前言 SQL 语言无处不在.SQL 已经不仅仅是技术人员的专属技能了,似乎人人都会写SQL,就如同人人都是产品经理一样.如果你是做后台开发的,那么CRUD就是家常便饭.如果你是做数仓开发的,那么写SQL可能占据了你的大部分工作时间.我们在理解 SELECT 语法的时候,还需要了解 SELECT 执行时的底层原理.只有这样,才能让我们对 SQL 有更深刻的认识.本文分享将逐步分解SQL的执行过程,希望对你有所帮助. 数据准备 本文旨在说明SQL查询的执行过程,不会涉及太复杂的SQL操作,主要涉及两

  • React Diff原理深入分析

    在了解Diff前,先看下React的虚拟DOM的结构 这是html结构 <div id="father"> <p class="child">I am child p</p> <div class="child">I am child div</div> </div> 这是React渲染html时的js代码   自己可以在babel上试试 React.createElemen

  • 详解Redis复制原理

    前言 本文主要介绍Redis复制机制 一.配置与实践 配置 Redis实例分为主节点(master)和从节点(slave),默认情况下都是主节点.每一个从节点只能有一个主节点,但是每一个主节点可以有多个从节点(注意数量,多个从节点会导致主节点写命令多次发送从而过度消耗网络带宽,可用树状结构降低主节点负载).复制是单向的,只能从主节点复制到从节点.配置复制的方式由以下3种: 在redis-slave.conf配置文件中加入slaveof {masterHost} {masterPort} 在red

  • 解析Redis Cluster原理

    目录 一.前言 二.为什么需要Redis Cluster 三.Redis Cluster是什么 四.节点负载均衡 五.什么是一致性哈希 六.虚拟节点机制 七.Redis Cluster采用的什么算法 八.Redis Cluster如何做到高可用 8.1.集群如何进行扩容 8.2.高可用及故障转移 九.简单了解gossip协议 十.gossip协议消息类型 十一.使用gossip的优劣 十二.总结 一.前言 Sentinel集群会对Redis的主从架构中的Redis实例进行监控,一旦发现了mast

  • Redis定时任务原理的实现

    目录 数据结构 常见操作 1.创建定时事件 2.触发定时事件 3.执行定时事件 总结 本文主要是基于 redis 6.2 源码进行分析定时事件的数据结构和常见操作. 数据结构 在 redis 中通过 aeTimeEvent 结构来创建定时任务事件,代码如下: /* Time event structure */ typedef struct aeTimeEvent {     // 标识符     long long id; /* time event identifier. */     //

  • Spring底层原理深入分析

    目录 bean生命周期 推断构造方法的底层原理 1.使用哪个构造方法 2.如果有参把哪个bean对象赋值给入参 AOP实现原理 spring事务 @Configuration 循环依赖 为什么会出现循环依赖 提前AOP 第一级缓存singletonObjects 第二级缓存earlySingletonObjects 第三级缓存singletonFactories bean生命周期 userService.class--->推断构造方法--->对象--->依赖注入--->初始化前(@

  • Redis核心原理详细解说

    目录 1.Redis为什么这么快 2.Redis网络模型 3.Redis数据结构 4.Redis持久化 RDB快照(snapshot) AOF(append-only file) RDB与AOF区别 Redis数据备份策略 5.Redis管道(Pipeline) 6.Redis使用lua脚本 7.Redis分布式锁 8.Redis主从架构 9.Redis哨兵架构 10.Redis集群 11.Redis优化 12.Redis问题 缓存穿透 缓存失效(击穿) 缓存雪崩 1.Redis为什么这么快 C

随机推荐