redis内存空间效率问题的深入探究


Posted in Redis onMay 17, 2021

前言

在使用redis时,我们会遇到一个问题,数据删除后,数据量已经不大了,但是使用top命令查看,还会发现redis占用了很对内存。实际上,因为数据删除后,redis释放内存由内存分配器管理,不会立刻返回给操作系统。所以,操作系统仍然记录着给redis分配了大量的内存

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

什么是内存碎片

通常情况下,内存空间利用率低,往往是因为操作系统发生了比较严重的内存碎片,那么什么是内存碎片呢?可以将内存看成是高铁上的作为,连续的空间相当于连座,内存碎片可以看成一个个零散的作为,如果你是3个人出行,火车上没有三个座位连着的,那么你就没法买到合适的作为,可能需要换一辆车

redis内存空间效率问题的深入探究

内存类似,如果需要申请一个N字节的连续空间,但是没有这么大的连续空间,那么,这些剩余空间就是内存碎片,redis内存碎片是什么原因导致的呢,了解了原因才有可能比较好的解决

内存碎片形成的原因

一般来说内存碎片形成的原因有两个,内因是操作系统的内存分配机制,外因是redis的负载特征

内因:内存分配器策略

内存分配器的分配策略就决定了操作系统无法做到“按需分配”。这是因为,内存分配器一般是按固定大小来分配内存,而不是完全按照应用程序申请的内存空间大小给程序分配。

Redis 可以使用 libc、jemalloc、tcmalloc 多种内存分配器来分配内存,默认使用 jemalloc。接下来,我就以 jemalloc 为例,来具体解释一下。其他分配器也存在类似的问题。

jemalloc 的分配策略之一,是按照一系列固定的大小划分内存空间,例如 8 字节、16 字节、32 字节、48 字节,…, 2KB、4KB、8KB 等。当程序申请的内存最接近某个固定值时,jemalloc 会给它分配相应大小的空间。

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

redis通常作为公共缓存和键值数据库对外提供服务,所以对于不同大小的数据,redis申请内存空间大小不一,这是一个外因。

因为内存分配是按照固定大小分配,所以内存空间一般都会比申请的空间大一些,所以本身就会有一些内存碎片,降低内存空间存储效率。

redis内存空间效率问题的深入探究

第二个外因是,这些数据会被删除和修改,会导致空间空间扩充和释放,具体来说,一方面,如果修改后的键值对变大或变小了,就需要占用额外的空间或者释放不用的空间。另一方面,删除的键值对就不再需要内存空间了,此时,就会把空间释放出来,形成空闲空间

redis内存空间效率问题的深入探究

一开始,应用 A、B、C、D 分别保存了 3、1、2、4 字节的数据,并占据了相应的内存空间。然后,应用 D 删除了 1 个字节,这个 1 字节的内存空间就空出来了。紧接着,应用 A 修改了数据,从 3 字节变成了 4 字节。为了保持 A 数据的空间连续性,操作系统就需要把 B 的数据拷贝到别的空间,比如拷贝到 D 刚刚释放的空间中。此时,应用 C 和 D 也分别删除了 2 字节和 1 字节的数据,整个内存空间上就分别出现了 2 字节和 1 字节的空闲碎片。如果应用 E 想要一个 3 字节的连续空间,显然是不能得到满足的。因为,虽然空间总量够,但却是碎片空间,并不是连续的。

好了,到这里,我们就知道了造成内存碎片的内外因素,其中,内存分配器策略是内因,而 Redis 的负载属于外因,包括了大小不一的键值对和键值对修改删除带来的内存空间变化。

如何判断是否有内存碎片

Redis 是内存数据库,内存利用率的高低直接关系到 Redis 运行效率的高低。为了让用户能监控到实时的内存使用情况,Redis 自身提供了 INFO 命令,可以用来查询内存使用的详细信息,命令如下:

INFO memory
# Memory
used_memory:1073741736
used_memory_human:1024.00M
used_memory_rss:1997159792
used_memory_rss_human:1.86G
…
mem_fragmentation_ratio:1.86O memory

INFO memory
# Memory
used_memory:1073741736
used_memory_human:1024.00M
used_memory_rss:1997159792
used_memory_rss_human:1.86G
…
mem_fragmentation_ratio:1.86

这里有一个 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 申请使用了 100 字节(used_memory),操作系统实际分配了 128 字节(used_memory_rss),此时,mem_fragmentation_ratio 就是 1.28。

那么,知道了这个指标,我们该如何使用呢?在这儿,我提供一些经验阈值:

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

如何清理内存碎片

当 Redis 发生内存碎片后,一个“简单粗暴”的方法就是重启redis实例,当然这并不是一个优雅的方法,重启会带来一些问题

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

幸运的是,从 4.0-RC3 版本以后,Redis 自身提供了一种内存碎片自动清理的方法,我们先来看这个方法的基本机制。还是通过一张图来看下

redis内存空间效率问题的深入探究

在进行碎片清理前,这段 10 字节的空间中分别有 1 个 2 字节和 1 个 1 字节的空闲空间,只是这两个空间并不连续。操作系统在清理碎片时,会先把应用 D 的数据拷贝到 2 字节的空闲空间中,并释放 D 原先所占的空间。然后,再把 B 的数据拷贝到 D 原来的空间中。这样一来,这段 10 字节空间的最后三个字节就是一块连续空间了。到这里,碎片清理结束。

需要注意:碎片清理事由代价的,操作系统需要把多份数据拷贝到新位置,把原有空间释放出来,这会带来时间开销。因为 Redis 是单线程,在数据拷贝时,Redis 只能等着,这就导致 Redis 无法及时处理请求,性能就会降低。而且,有的时候,数据拷贝还需要注意顺序,就像刚刚说的清理内存碎片的例子,操作系统需要先拷贝 D,并释放 D 的空间后,才能拷贝 B。这种对顺序性的要求,会进一步增加 Redis 的等待时间,导致性能降低。

那么,有什么办法可以尽量缓解这个问题吗?这就要提到,Redis 专门为自动内存碎片清理功机制设置的参数了。我们可以通过设置参数,来控制碎片清理的开始和结束时机,以及占用的 CPU 比例,从而减少碎片清理对 Redis 本身请求处理的性能影响。

首先,Redis 需要启用自动内存碎片清理,可以把 activedefrag 配置项设置为 yes,命令如下:

config set activedefrag yes

这个命令只是启用了自动清理功能,但是,具体什么时候清理,会受到下面这两个参数的控制。这两个参数分别设置了触发内存清理的一个条件,如果同时满足这两个条件,就开始清理。在清理的过程中,只要有一个条件不满足了,就停止自动清理。

  • active-defrag-ignore-bytes 100mb:表示内存碎片数量达到100MB时,开始清理
  • active-defrag-threshold-lower 10:表示内存碎片空间占操作系统给redis分配空间的10%时开始清理

为了尽可能减少碎片清理对 Redis 正常请求处理的影响,自动内存碎片清理功能在执行时,还会监控清理操作占用的 CPU 时间,而且还设置了两个参数,分别用于控制清理操作占用的 CPU 时间比例的上、下限,既保证清理工作能正常进行,又避免了降低 Redis 性能。这两个参数具体如下:

  • active-defrag-cycle-min 25:表示自动清理过程cpu时间笔记不低于25%,保证清理能正常开展
  • active-defrag-cycle-max 75:表示自动清理过程所用 CPU 时间的比例不高于 75%,一旦超过,就停止清理,从而避免在清理时,大量的内存拷贝阻塞 Redis,导致响应延迟升高。

自动内存碎片清理机制在控制碎片清理启停的时机上,既考虑了碎片的空间占比、对 Redis 内存使用效率的影响,还考虑了清理机制本身的 CPU 时间占比、对 Redis 性能的影响。而且,清理机制还提供了 4 个参数,让我们可以根据实际应用中的数据量需求和性能要求灵活使用,建议你在实践中好好地把这个机制用起来。

总结

到此这篇关于redis内存空间效率问题的文章就介绍到这了,更多相关redis内存空间效率内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Redis 相关文章推荐
Redis可视化客户端小结
Jun 10 Redis
解析Redis Cluster原理
Jun 21 Redis
Redis如何实现分布式锁
Aug 23 Redis
为什么RedisCluster设计成16384个槽
Sep 25 Redis
解决linux下redis数据库overcommit_memory问题
Feb 24 Redis
高并发下Redis如何保持数据一致性(避免读后写)
Mar 18 Redis
使用Redis实现点赞取消点赞的详细代码
Mar 20 Redis
浅谈Redis 中的过期删除策略和内存淘汰机制
Apr 03 Redis
windows安装 redis 6.2.6最新步骤详解
Apr 26 Redis
解决 redis 无法远程连接
May 15 Redis
浅谈Redis的事件驱动模型
May 30 Redis
深入浅析Redis 集群伸缩原理
Redis延迟队列和分布式延迟队列的简答实现
基于Redis延迟队列的实现代码
基于Redis实现分布式锁的方法(lua脚本版)
redis三种高可用方式部署的实现
May 11 #Redis
Redis数据结构之链表与字典的使用
基于Redis位图实现用户签到功能
May 08 #Redis
You might like
php后台程序与Javascript的两种交互方式
2009/10/25 PHP
PHP面向对象精要总结
2014/11/07 PHP
php实现留言板功能(代码详解)
2017/03/28 PHP
Laravel框架实现redis集群的方法分析
2017/09/14 PHP
为你的 Laravel 验证器加上多验证场景的实现
2020/04/07 PHP
Extjs 继承Ext.data.Store不起作用原因分析及解决
2013/04/15 Javascript
公共js在页面底部加载的注意事项介绍
2013/07/18 Javascript
浅析return false的正确使用
2013/11/04 Javascript
jquery与prototype框架的详细对比
2013/11/21 Javascript
js读写cookie实现一个底部广告浮层效果的两种方法
2013/12/29 Javascript
js中的caller和callee属性介绍和例子
2014/06/07 Javascript
动态加载js的方法汇总
2015/02/13 Javascript
七夕情人节丘比特射箭小游戏
2015/08/20 Javascript
jQuery中队列queue()函数的实例教程
2016/05/03 Javascript
javascript的replace方法结合正则使用实例总结
2016/06/16 Javascript
Javascript对象字面量的理解
2016/06/22 Javascript
Bootstrap框架下下拉框select搜索功能
2020/03/26 Javascript
Node.js配合node-http-proxy解决本地开发ajax跨域问题
2016/08/31 Javascript
AngularJS 表达式详解及实例代码
2016/09/14 Javascript
javascript中json基础知识详解
2017/01/19 Javascript
Bootstarp基本模版学习教程
2017/02/01 Javascript
js实现截图保存图片功能的代码示例
2017/02/16 Javascript
vue的常用组件操作方法应用分析
2018/04/13 Javascript
如何使用vuex实现兄弟组件通信
2018/11/02 Javascript
vue v-for出来的列表,点击某个li使得当前被点击的li字体变红操作
2020/07/17 Javascript
纯用NumPy实现神经网络的示例代码
2018/10/24 Python
用OpenCV将视频分解成单帧图片,图片合成视频示例
2019/12/10 Python
iPython pylab模式启动方式
2020/04/24 Python
基于CSS3实现图片模糊过滤效果
2015/11/19 HTML / CSS
英国医生在线预约:Top Doctors
2019/10/30 全球购物
美国购买舞会礼服网站:Couture Candy
2019/12/29 全球购物
Windows和Linux动态库应用异同
2016/07/28 面试题
餐厅销售主管职责范本
2014/02/19 职场文书
第二批党的群众路线教育实践活动个人对照检查材料
2014/09/23 职场文书
《蟋蟀的住宅》教学反思
2016/02/17 职场文书
详解Redis在SpringBoot工程中的综合应用
2021/10/16 Redis