作者|莱乌

作为内存数据库,内存空间大小对于 Redis 来说是至关重要的。内存越多,意味着存储的数据也会越多。但是不知道你有没有遇到过这样的情况,明明空间很大,但是内存的使用却不是很理想。

为什么会出现这样的情况呢?这期我们就来看看这个"诡异"的事件。

坐好了,准备发车!

- 思维导图 -

查看内存使用情况

首先想要知道 Redis 内存的使用情况,我们就需要获取相关的信息。

Redis 中查看内存相关信息是很简单的,只需要在命令行输入『info memory』就可以看到各种相关数据。在这里我罗列了一些较为重要的参数:

  • used_memory:已经使用了的内存大小。

  • used_memory_rss:redis 物理内存的大小。

  • mem_fragmentation_ratio:内存碎片率。

这里有一个内存碎片率的名词需要关注下,它可以用来表示当前的内存使用情况。

具体计算方式:

对于内存碎片率,一般保持在 1~1.5 之间是最合理的。

什么是内存碎片

了解了内存碎片率,那什么是内存碎片呢?

定义是这样的:由于一块连续空闲的空间比所要申请的空间小,导致这块空间不可用,对于内存整体来说就是内存碎片。

举个例子:

假设有一块 100MB 的连续空闲内存空间,你每次都会从中申请一块 30MB 的内存。那么当你申请了 3 次后,这块内存就只剩下了 10MB 的空间,第 4 次申请的时候就会失败。如果没有其它的空间释放并且每次申请的空间都比 10MB 大,那么剩下的空间对于整块内存来说就是内存碎片。

内存碎片导致的原因

Redis 中,最常用的是写入、修改、删除数据。这些操作在执行后都会产生 一定程度的内存碎片。

写入数据

Redis 中分配内存是根据固定的大小来划分内存空间的。为了减少分配次数,Redis 会根据申请的内存最接近的固定值分配相应大小的空间。

什么意思呢,假如 Redis 按照 8 字节、16 字节、32 字节、48 字节等来分配内存。当你想要存储一个 18 字节的数据时,此时 Redis 就会分配 32 字节(因为 32 是与 18 最接近的固定值)。如果这时候,再写入的数据需要的内存空间在 14 个字节内,那 Redis 就无需再进行分配了。

这就像你有不同的箱子,为了装东西,你需要找一个体积最接近的箱子来装。但是装进去后,你发现还有空间可以放一些小东西,就无需再找箱子了。

但是,这种分配空间的方式会带来一定程度的内存碎片。我们可以把固定大小的划分空间看成不同体积的箱子,每种箱子里的空间不同程度上都会有剩余。这些剩余的空间就是内存碎片。

修改数据

键值对进行修改时,可能会变大也会变小,相应的就会占用额外空间或者释放不用的空间。

如图中所示,当前 A、B、C 分别占用了 3、2、4 个字节,将 A 从 3 字节修改为 2 字节时,此时就会有 1 个字节的空间空了出来,这时就会出现 1 个字节的碎片。

那如果我将数据 A 从 3 字节修改为 4 字节呢?此时为了保持数据 A 的空间连续性,操作系统会把 B 拷贝到别的空间。此时又会出现 1 个字节的碎片。

删除数据

理解了修改数据,删除数据就很容易明白了。还是上边的例子,此时删除了数据 B,那么就释放了 2 个字节的空间。这样对于整个内存空间来说就产生了 2 个字节的碎片。

如何解决内存碎片

你可能会有疑问,内存碎片会有什么危害呢?

我们还是以上边的箱子来表示。你想想,如果你要把这些箱子都装上车运走,每个箱子里都有空出来的空间(内存碎片),那么运行一次的效率及性价比是不是会很低。同样,在 Redis 中,由于大量的碎片存在,会导致实际利用率变低。

那么我们有没有办法来解决内存碎片呢?

推倒重来

第一种方式很简单,直接推倒重来。也就是把 Redis 直接重启完事儿,内存一断电全世界就清净。但是这种暴力省事的方式却有很多隐患。

生产环境中你这么搞的话得提前烧烧香,保佑不会出什么问题。如果你没进行过持久化,那么就别烧了,烧了也没用。如果有持久化的话,那么恢复时长还得取决你持久化文件的大小,在这个阶段还无法提供服务。糟心不?

空间置换

那么有没有不这么刺激的方式。

有的,高版本的 Redis 提供了内存碎片清理的方式。一言以蔽之,就是空间置换。

怎么个置换法?我们的目的是为了消除内存碎片,那么我们把已使用的内存数据重新整理到一起不就行了吗?让不连续的空间变成连续的,剩下的空间,继续来分配。

画个图理解下:

但是,说说还是挺容易的,理论到实践中间还隔着性能损耗。

在进行多次数据拷贝过程中,单线程的 Redis 只能干等着,无法响应客户端的请求。这时候只能干瞪眼,性能太受影响。

凉,那该咋整?!别急,有缓解的策略,你接着往下看。

Redis 中有专门的参数设置用来进行自动清理内存碎片:activedefrag yes

这个命令是启动清理功能的,这还不够,Redis 中还需要其他的条件限制才能够进行清理。

下面参数都是满足任一条件后就可以进行清理:

  • active-defrag-ignore-bytes 100mb

    碎片达到100MB时,开启清理。

  • active-defrag-threshold-lower 10

    当碎片超过 10% 时,开启清理。

  • active-defrag-threshold-upper 100

    内存碎片超过 100%,尽最大清理。

在处理的过程中,为了避免对正常请求的影响,同时又能保证性能。Redis 同时还提供了监控 CPU 占用比例的参数,在满足以下条件时才会保证清理正常开展:

  • active-defrag-cycle-min 5

清理内存碎片占用 CPU 时间的比例不低于此值,保证清理能正常开展。

  • active-defrag-cycle-max 75

清理内存碎片占用 CPU 时间的比例不高于此值。一旦超过则停止清理,从而避免在清理时,大量的内存拷贝阻塞 Redis,导致其它请求延迟。

总结

查看内存使用情况

  • 在命令行执行 info memory 即可查看 Redis 内存相关信息。根据内存碎片率可以在一定时机内进行清理碎片清理。

内存碎片导致原因

  • 写入数据时,Redis 为了减少分配次数在分配内存是根据固定的大小来划分内存空间的。修改数据时会释放或占用额外的内存空间,删除数据时会释放空间。这样就会产生不同程度的内存碎片。

如何解决内存碎片

  • 通过重启 Redis 的方式进行处理,如果没有持久化可能会导致事故。在持久化情况下,恢复速度需要取决于文件的大小。

  • 通过空间置换方式,也就是将已使用的内存数据重新整理到一起。

特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:

长按订阅更多精彩▼如有收获,点个在看,诚挚感谢

败家玩意儿!Redis 竟然浪费了这么多内存!相关推荐

  1. [原创]Redis BUG系列(I)——SDS字符串的sdssplitlen函数实现顺序BUG,浪费了计算与内存资源

    这里写自定义目录标题 简要 BUG介绍 源码与说明 BUG内容 修改方法 BUG验证 BUG影响 版本范围 哪些时候调用sdssplitlen这个函数? 我在GIT上提交的pull request 简 ...

  2. Redis 过期键删除策略、内存淘汰机制

    文章目录 过期键删除策略 定时删除 惰性删除 定期删除 Redis的选择 内存淘汰机制 redis中缓存的数据是有过期时间的,当缓存数据失效时,redis会删除过期数据以节省内存,那redis是怎样怎 ...

  3. 高性能分布式缓存redis(持久化原理 安全策略 过期删除内存淘汰策略 性能压测 高可用 Redis Cluster)

    redis redis(持久化原理 安全策略 过期删除&内存淘汰策略 性能压测 高可用 Redis Cluster) 1. 持久化原理 1.1 持久化流程(落盘) 1.2 RDB详解 1.2. ...

  4. redis value多大会影响性能_选择合适Redis数据结构,减少80%的内存占用

    前言 redis作为目前最流行的nosql缓存数据库,凭借其优异的性能.丰富的数据结构已成为大部分场景下首选的缓存工具. 由于redis是一个纯内存的数据库,在存放大量数据时,内存的占用将会非常可观. ...

  5. redis mysql windows_Redis+Mysql模式和内存+硬盘模式的异同

    学习任何新知识,都是一个循序渐进的过程,从刚开始的懵懂无知,到简单熟悉,然后突然的彻悟,成果让人欣喜若狂,心情也会快乐很久. redis+mysql和内存+硬盘类似的地方 首先看图: 首先,我们知道, ...

  6. redis 删除数据,但是占用内存没有下降原因及解决办法

    在使用 Redis 时,我们经常会遇到这样一个问题:明明做了数据删除,数据量已经不大了,为什么使用 top 命令查看时,还会发现 Redis 占用了很多内存呢? 实际上,这是因为,当数据删除后,Red ...

  7. 如何使用Redis Data Reveal(rdr)查看redis中每个key占用的内存大小

    如何使用Redis Data Reveal(rdr)查看redis中每个key占用的内存大小 一.为什么要查看redis中每个key的占用 二.怎么查看redis中每个key的占用 一.为什么要查看r ...

  8. 这款刷爆抖音的玩意儿,竟然是程序员的福音!

    当代80.90后的颈椎多多少少都有点问题 毕竟我们是被称做低头族的一代 虽然大家呼吁我们放下手机多出去走走 看看周围的风景 我们也曾经尝试过放下过手机 但是最后发现还是手机最好玩 当我们这一代成为了上 ...

  9. 利用TCMalloc替换Nginx和Redis默认glibc库的malloc内存分配

    TCMalloc的全称为Thread-Caching Malloc,是谷歌开发的开源工具google-perftools中的一个成员.与标准的glibc库的Malloc相比,TCMalloc库在内存分 ...

最新文章

  1. 数据结构与算法:15 树
  2. sqlite 实例教程 IOS下用sqlite打造词典
  3. java工程前面有个红色感叹号
  4. ASP.NET Core官方计划路线及需要废除的一些Framework技术
  5. 今日头条野心背后逃不过的10个问题
  6. python2.7 跨文件全局变量的方法
  7. 上传书籍进度信息到服务器...,使用HttpWebRequest实现大文件上传资料.pdf
  8. 安卓真机如何连接本地服务器_一分钟搭建可供手机访问的本地服务器 (安卓,ios手机通用)...
  9. python队列怎么用_如何在Python中使用多处理队列? - python
  10. Spring Data JPA 从入门到精通~SpEL表达式的支持
  11. 如何绘制逻辑图 — 5. 要素的属性:系统与模块
  12. 数据安全:英国公司泄露超过100万人的指纹和面部识别数据
  13. python 多进程 调用模块内函数_python--多进程的用法详解实例
  14. C# 中的readonly属性
  15. Typora如何设置图片的默认保存路径
  16. 值得推荐的C/C++开源框架和库
  17. HDU 2899 :(二分求最小值)
  18. atitit.元编程总结 o99
  19. 超级搜索术,提升解决问题的能力
  20. 分数乘法计算机题,分数乘法计算题100道测试卷(无答案)

热门文章

  1. AttributeError: ‘Tensor‘ object has no attribute ‘encoder‘
  2. poj1716(差分约束+SPFA)
  3. 思维dp ---- CF41D Pawn [可达状态统计dp]
  4. luogu P3393 逃离僵尸岛(点权最短路 + 多源BFS)
  5. 非二叉树 UVA297 四分树 Quadtrees
  6. python并发1000个http请求_php下api接口的并发http请求
  7. from name as id为啥报错
  8. c++ 调用labview_LabVIEW面向对象编程_初窥门径(5):开发方式漫谈
  9. 玻利维亚java_BlogJava
  10. vue cli根据不同的环境打包