目录

前言
案例现象
定位问题
内存回收策略
键过期机制
slave的过期策略
解决问题

前言

我们知道,Redis是一个key-value数据库,它的数据是运行在内存中的

其读写效率比将数据存储到磁盘上的数据库要快很多

虽然性能强大,但是如果我们不了解Redis的内存回收策略,就有可能导致Redis消耗内存过高甚至导致内存溢出,严重影响系统性能

案例现象

发现生产环境上的一台服务器出现内存使用率达到阈值的告警

登上服务器使用 top 命令先看下
系统资源整体使用的情况

top
通过 top 的输出发现:

系统平均负载没有异常
系统cpu使用率没有异常
系统已使用的物理内存(used)数值特别高,达到了总物理内存的80%以上
而且buffer/cache的数值也不小,这说明有应用产生了大量的读写缓存
光看系统资源整体使用情况不能精确的定位到问题

我们继续观察 top 输出,这次我们将重点放到了各个进程的资源使用情况

发现:

redis进程占用了最多的内存,达到了20G
redis进程的使用率也达到了90%以上
由 top 的输出我们不难发现,这台服务器上的 redis实例消耗了大量的内存,而且cpu使用率很高,应该是有应用往 redis 上进行大量的读写操作

定位问题

这台服务器上的 Redis 是运行在 docker 上的,我们进入到 redis 容器里面

docker exec -it redis /bin/bash 
我们还可以使用如下命令来查看 redis 容器的一些详细信息

docker inspect redis
然后找到 Redis 的配置文件路径,检查一下是不是配置出了问题

与对部署相同服务的服务器redis配置文件对比了一下,发现配置并没有什么问题

不是配置问题,那到底是什么原因导致Redis占用了这么多内存?这些使用的内存Redis不会回收的吗?

在回答这些问题时,我们先来了解一下Redis的内存回收策略以及键过期机制

内存回收策略

Redis是基于内存的数据库,常被用作缓存,以此来提高系统的响应速率与性能


Redis内存消耗

Redis进程的内存消耗主要包括:自身内存 + 对象内存 + 缓冲内存 + 内存碎片

自身内存

一般来讲,Redis空进程自身内存消耗非常少,通常 usedmemoryrss 在 3MB 左右时,used_memory 一般在 800KB 左右

一个空的 Redis 进程消耗内存可以忽略不计

对象内存
对象内存是Redis内存占用最大的一部分,因为它存储着用户所有的数据

对象内存消耗可以简单理解为这两个对象(Key和Value)的内存消耗之和(还有类似过期之类的信息)

使用 Redis 时很容易忽略键对内存消耗的影响,应当避免使用过长的键以及给键设置一个过期时间

缓冲内存
缓冲内存主要包括客户端缓冲、复制积压缓冲和AOF缓冲

客户端缓冲指的是所有接入到 Redis 服务器 TCP 连接的输入输出缓冲

复制积压缓冲区是Redis 在 2.8 版本后提供的一个可重用的固定大小缓冲区,用于实现部分复制功能,根据 repl-backlog-size 参数控制,默认 1MB。对于复制积压缓冲区整个主节点只有一个,所有的从节点共享此缓冲区。因此可以设置较大的缓冲区空间,比如说 100MB,可以有效避免全量复制

AOF 重写缓冲区这部分空间用在AOF 重写期间保存最近的写入命令。AOF 重写缓冲区的大小用户是无法控制的,取决于 AOF 重写时间和写入命令量,不过一般都很小

内存碎片
Redis内部有自己的内存管理器,为了提高内存使用的效率,实现对内存的申请和释放进行管理。

Redis中的值删除的时候,并没有把内存直接释放交还给操作系统,而是交给了Redis内部有内存管理器。这就使得如果大量的key在短时间内过期被删除,这些内存不会释放给操作系统,而是交给内部内存管理器, 从而导致redis实际占用的内存与申请的内存相差过大,就会导致大量的内存碎片

Redis 正常碎片率一般在 1.03 左右

PS:子进程内存消耗

除此之外,Redis实例的内存消耗还有一部分是子进程的内存消耗,子进程内存消耗主要指执行 AOF 重写 或者进行 RDB 保存时 Redis 创建的子进程内存消耗

Redis 内存相关的指标

我们可以通过info memory 命令可以获得 Redis 内存相关的指标

127.0.0.1:6379 > info memory

这里我们挑三个比较重要的字段来讲一下

mem_fragmentation_ratio
当该值 > 1时,说明有部分内存并没有用于数据存储,而是被内存碎片所消耗,如果该值很大,说明碎 片率严重

当该值 < 1时,一般是因为操作系统把 Redis 内存中的数据 swap 到硬盘里面,出现这种情况要格外关注,因为硬盘速度远远慢于内存,所以 Redis 性能会变得很差,甚至僵死

建议设置和内存一样大小的交换区,如果没有交换区,一旦 Redis 突然需要的内存大于当前操作系统可用内存时,Redis 会因为内存溢出而被内核的 OOM 机制直接杀死

maxmemory
Redis 使用 maxmemory 参数限制最大可用内存。限制内存的目的主要有:

1、用于缓存场景,当超出内存上限 maxmemory 时使用 LRU 等回收策略释放空间

2、防止所用的内存超过服务器物理内存,导致 OOM 后进程被系统杀死

maxmemory 限制的是 Redis 实际使用的内存量,也就是 used_memory 对应的内存。

实际消耗的内存可能会比 maxmemory 设置的大,要小心因为这部分内存导致 OOM。所以,如果你有 10GB 的物理内存,最好将 maxmemory 设置为 8 或者9G

maxmemory_policy
Redis默认采用noeviction策略

volatile-lru:
# 在设置了过期时间的所有键中,选取最近最少使用的数据删除

volatile-lfu:
# 在设置了过期时间的所有键中,选取最近最不常用,也就是一定时期内被访问次数最少的数据删除

volatile-random:
# 筛选出设置了过期时间的键值对,随机删除。

volatile-ttl:
# 筛选出设置了过期时间的键值对,越早过期的越先被删除。

allkeys-lru:
# 在所有键中,选取最近最少使用的数据删除

allkeys-lfu:
# 在所有键中,选取最近最不常用,也就是一定时期内被访问次数最少的数据删除

allkeys-random:
# 采用随机淘汰策略删除所有的键值对,这个策略不常用。

noeviction:
# 不淘汰任何键值对,当内存满时,如果进行读操作,例如get命令,它将正常工作,
# 如果做写操作,它将返回错误,
# 也就是说,当Redis采用这个策略内存达到最大的时候,它就只能读不能写了


键过期机制

除了上面提到的内存回收机制可以有效解决 Redis 内存使用过高的问题之外,Redis还有一个键过期机制——给key设置一个过期时间,一旦超过过期时间,这个key就会被被删除,内存将被回收

PS:前面一章说的是是内存不足的「回收策略」,这一种是过期键的删除策略,两者是不同的,不要搞混了

我们来看一下相关的命令

查看key的过期时间

如果key存在过期时间,返回剩余生存时间(秒);如果key是永久的,返回-1;如果key不存在或者已过期, 返回-2

TTL单位是秒,PTTL单位是毫秒

127.0.0.1:6379> TTL KEY
127.0.0.1:6379> PTTL KEY
设置key的过期时间

设置一个key在当前时间"seconds"(秒)之后过期。返回1代表设置成功,返回0代表key不存在或者无法设 置过期时间

EXPIRE单位是秒,PEXPIRE单位是毫秒

语法:EXPIRE key seconds
127.0.0.1:6379> EXPIRE name 60
(integer) 1
设置一个key在"timestamp"(时间戳(秒))之后过期。返回1代表设置成功,返回0代表key不存在或者无法 设置过期时间

EXPIREAT单位是秒,PEXPIREAT单位是毫秒

语法:EXPIREAT key "timestamp"
127.0.0.1:6379> EXPIREAT name 1586941008
(integer) 1
SETEX在逻辑上等价于SET和EXPIRE合并的操作,区别之处在于SETEX是一条命令,而命令的执行是原子性 的,所以不会出现并发问题

语法:SETEX key "seconds" "value"
127.0.0.1:6379> SETEX name 100 jack
OK
给key设置了过期时间,如果 key 过期了 Redis 该怎么处理呢?

Redis 过期 Key 处理

Redis key过期处理的方式有三种:

1、惰性删除

不管键有没有过期都不主动删除,等到每次去获取键时再判断是否过期,如果过期就删除该键,否 则返回键对应的值。这种策略对内存不够友好,可能会浪费很多内存

缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄 露(无用的数据占用了大量的内存)

2、定时删除

在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对 key进行删除

缺点:定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器 产生),性能影响严重,因为每个定时器都会占用一定的 CPU 资源

3、定期删除

系统每隔一段时间就定期扫描一次,发现过期的键就进行删除

以下两种方式可以触发定期删除:

配置redis.conf 的hz选项,默认为10 (即1秒执行10次,100ms一次,值越大说明刷新频率 越快,最Redis性能损耗也越大)
配置内存回收策略,当Redis消耗内存达到最大内存使用限制,就会自行对应的策略,来对过期key进行删除

在 Redis 当中,其选择的是策略 2 和策略 3 的综合使用。不过 Redis 的定期删除只会扫描设置了过期时间的键,因为设置了过期时间的键 Redis 会单独存储,所以不会出现扫描所有键的情况

同一时间大量Key过期会有什么影响?

Redis 是单线程的,收割的时间也会占用线程的处理时间,如果收割的太过于繁忙,以至于忙不过来?会不会导致线上读写指令出现卡顿?

Redi将每个设置了过期时间的Key放入到一个独立的字典中,会定时遍历这个字典来删除,默认会每秒进行十次过期扫描,过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略

从过期字典中随机 20 个 key;
删除这 20 个 key 中已经过期的 key;
如果过期的 key 比率超过 1/4,那就重复步骤 1
为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间的上限,默认不会超过 25ms

#如果Redis 实例中所有的 key (几十万个)在同一时间过期会怎样?
Redis会持续扫描过期字典(循环),知道过期字典中的过期key变得稀疏,才会降低扫描次数
内存管理器需要频繁回收内存页,此时会产生一定的CPU消耗,必然会导致线上读写请求出现明显卡顿的现象

当客户端请求到来时(服务器如果正好进入过期扫描状态),请求将会至少等待25ms才会进行处理,入锅客户端将超时时间设置的比较短(10ms),那么就会出现大量的连接因为超时而关闭,业务端就会出现很多异常,而且这时你还无法从Redis的slowlog中看到慢查询记录

slave的过期策略

从库不会进行过期扫描,从库对过期的处理是被动的

当master采用定期或惰性删除过期键时,会同步一个del操作到slave,这样从库也可以删除过期key,但是salve从不会自己处理过期key,只会应用master同步过来的del操作

也就是说即使键slave已经过期了,slave也不会自己处理过期后如果主库不同步DEL操作过来,那么从库并不会采用主动或惰性的方式去清理过期键

这样就会造成一个问题:

slave是提供读服务的,如果客户端在slave上读取了一个过期的key,而且master没有及时地处理,那么客户端仍能读取到

这个问题在Redis3.2以下会存在,但之后Redis进行了优化:如果客户端在slave读取到了过期的key,再发起读请求的时候,Redis会判断这个key是否过期,如果过期则返回nil

但是slave依旧不会对过期key进行任何处理,而是等待maser同步del操作

RDB对过期Key的处理

持久化数据到RDB文件
持久化之前会检查key是否过期,过期的key不进入RDB文件

从RDB文件恢复数据
数据载入数据库之前,会对key进行过期检查,如果过期则不导入数据库(主库)
如果RDB文件里有过期的键,那还是会载入,但是主从在数据同步时(全量复制),slave的数据会被清空(丢弃原先所有数据),所以不影响

AOF对过期Key的处理

持久化数据到AOF文件
如果某个key过期,还没有被删除,该key是不会进入aof文件的,因为没有发生修改命令
当key过期被删除后,就会向aof文件追加一条del命令(在将来的以aof文件恢复数据的时候该过期的键就会被删掉)

AOF重写
重写时,会先判断key是否过期,已过期的key不会重写到aof文件

如果给键设置了较大的过期时间且没有使用内存回收策略,原本的数据没过期不会被回收,又不断写入新的数据,这样会导致 Redis 消耗的内存不断增大

解决问题

了解了 Redis 的内存回收策略以及键过期机制之后我们分别来看一下

我们首先看一下任意key的过期时间是多少

#从当前数据库中随机返回一个 key
127.0.0.1:6379> RANDOMKEY

127.0.0.1:6379> TTL key
(integer) 12032145
我们发现,key的过期时间设置成了一千多万秒!这个过期时间也太长了吧

再查看一下 Redis 的内存回收策略配置

127.0.0.1:6379> info memory
maxmemory:0
maxmemory_policy:"noeviction"
可以看到,我们并没有设置内存最大限制,而且内存回收策略是noeviction,即不淘汰任何键值对

查看了这两个选项之后,问题就清晰起来了:

key的过期时间设置的太长,没有设置最大可用内存限制而且内存回收策略是noeviction 就会使得原先的数据还没过期,又有新的数据写进来,导致消耗内存越来越多,而系统又无法进行回收

解决方法

1、重新给键设置过期时间

这个不太现实,因为生产环境中的 Redis 有大量的 Key(十万级甚至百万级),不可能说一个一个的重新设置过期时间

而且我们的 Redis是运行在docker上的,已经打包成一个容器,如果修改的话需要花费大量的时间和精力

2、修改配置文件(推荐使用)

我们可以更改 Redis 的配置,设置最大内存使用限制以及内存回收策略

修改 Redis 配置文件,添加如下两个字段:

maxmemory:10G
maxmemory_policy:"volatile-lru"
我们设置了最大内存使用限制为10G,一旦redis占用内存超过10GB,就会触发内存回收机制volatilelru——在设置了过期时间的key里,删除最近最少使用的key

更改配置后重启一下,等待一段时间后发现 Redis 消耗的内存降下去了,也不再告警了

Redis占用内存过高怎么办相关推荐

  1. redis 内存不足 排查_排查redis占用内存达90%以上

    帮别人排查一个问题,项目还没上线但redis占用内存很高.思路如下: 1.登陆redis控制台,首先用 keys * 获取所有的key > keys * x:x:a x:x:b x:x:c 发现 ...

  2. Java进程占用内存过高,排查解决方法

    Java进程占用内存过高,排查解决方法 参考文章: (1)Java进程占用内存过高,排查解决方法 (2)https://www.cnblogs.com/eeexu123/p/10913389.html ...

  3. java内存问题怎么排查,java占内存高排查 java应用占用内存过高排查的解决方案...

    想了解java应用占用内存过高排查的解决方案的相关内容吗,zhaixing_0307在本文为您仔细讲解java占内存高排查的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:java,占内存过 ...

  4. antimalware service executable占用内存过高_SQLServer占用服务器内存过高,更改这个设置就能降低内存使用率

    前两天我的服务器突然告警提示我的内存占用过高,使用率高达94.3%.当时我就有点纳闷了,服务器配置 互联网服务器 是4核8G的,只装了4个应用程序,其中就有SQL SERVER2012,而且使用频率也 ...

  5. 电脑一开机内存(共8G)就用了70%以上,任务管理器里面查看没有占用内存很高的进程

    我的解决方法: 1. 安装360驱动大师 2. 点击全面诊断 3. 驱动更新 4. 360优化加速-->启动项管理-->禁止不必要的启动项. 我执行了以上3项之后,笔记本的内存占用变小了. ...

  6. 桌面内存管理器(dwn.exe)占用内存过高时怎么办?

    桌面内存管理器(dwn.exe)占用内存过高,可以解决的办法多种多样. 桌面内存管理器(dwn.exe)占用内存过高主要是因为存在DWM内存泄漏, 也正是因为这样,DWN内存泄露,不会自动回收内存,导 ...

  7. 笔记本桌面窗口管理器占用内存过高怎么办?

    桌面窗口管理器占用内存过高怎么办?很多小伙伴们在使用电脑时,明明没有打开多少程序,却莫名奇妙出现内存占用过高的情况,这样十分影响电脑的使用,还可能对cpu造成损害,但是小伙伴们也不知道如何解决,那么今 ...

  8. win11占用内存太高怎么解决?

    win11占用内存太高怎么办?win11系统对于系统的占用量会更大.很多的用户安装了这个系统之后都有遇到内存被大量占用的情况.那么我们如何通过系统内部优化的方法来解决这个问题呢?今天我们一起来看看解决 ...

  9. 解决svchost占用内存过高问题

    摘抄自:https://jingyan.baidu.com/article/d169e1867cea7e436611d801.html svchost占用内存过高,会导致内存100%电脑卡住,CPU温 ...

最新文章

  1. 青源 LIVE 预告 | McGill李岳Mila唐建团队新作:可迁移、可解释的单细胞RNA测序模型...
  2. 识别网络应用所使用的协议Amap
  3. 天津财经计算机专业研究生分数线,天津财经大学各专业2015—2020年硕士研究生复试分数线汇总...
  4. js 正则是否包含某些字符串_js 判断字符串中是否包含某个字符串(转载)
  5. 华为诺亚方舟预训练语言模型NEZHA、TinyBERT开源代码
  6. 如何删除旧的和未使用的Docker映像
  7. mysql创建表shop_ShopXO商城-支付方式 - 数据库设计 - 数据库表结构 - 果创云
  8. 机器学习资料合计(一)
  9. 含泪推荐5款WIN10装机必备的软件
  10. blender烘焙法线贴图
  11. 计算机毕业论文任务书模板,计算机毕业论文设计任务书范文计算机系毕业论文任务书上的方法写.doc...
  12. Java POI 删除最后一页空白页
  13. 关于AD中如何绘制原理图及其封装
  14. 【没有刀剑,如何行走江湖】半晌私语(上)
  15. 智能管家App kotlin版(2)——工具类封装与首页引导页开发
  16. BUUCTF-Misc-No.4
  17. c++ uchar float转换
  18. asterisk简单实用
  19. Instruction Set Principles
  20. 139.深度学习分布式计算框架-2

热门文章

  1. p语言是python吗-python编程语言是什么?它能做什么?
  2. C语言二级题库(卷一)
  3. 视频监控系统 摄像头与网络存储服务器兼容,网络视频监控系统技术要点
  4. 微信小程序recycle-view  走过的坑
  5. “大数据杀熟”?商家对数据的使用可能远超出想象
  6. VS2019即将完成…一切即将准备就绪
  7. 网上收集的一些程序员笑话
  8. 知之道,达有余而通不足;行之道,嬴有余以弥不足。知行合一,亦乎 如是。
  9. 创建一个Rectangle类。 添加两个属性width、height,分别表示宽度和高度,添加计算矩形的周长和面积的方法。测试输出一个矩形的周长和面积。
  10. python、pandas、Excel、Powerbi中对日期的处理方法