当前位置: 首页 > news >正文

Redis 内存回收

文章目录

  • 1. 过期key处理
    • 1.1 惰性删除
    • 1.2 周期删除
  • 2. 内存淘汰策略


在这里插入图片描述

Redis 中数据过期策略采用定期删除+惰性删除策略结合起来,以及采用淘汰策略来兜底。

定期删除策略:Redis 启用一个定时器定时监视所有的 key,判断key是否过期,过期的话就删除。这种策略可以保证过期的 key 最终都会被删除,但是也存在严重的缺点:每次都遍历内存中所有的数据,非常消耗 CPU 资源,并且当 key 已过期,但是定时器还处于未唤起状态,这段时间内 key 仍然可以用。

惰性删除策略:在获取 key 时,先判断 key 是否过期,如果过期则删除。这种方式存在一个缺点:如果这个 key 一直未被使用,那么它一直在内存中,其实它已经过期了,会浪费大量的空间。

这两种策略天然的互补,结合起来之后,定时删除策略就发生了一些改变,不在是每次扫描全部的 key 了,而是随机抽取一部分 key 进行检查,这样就降低了对 CPU 资源的损耗,惰性删除策略互补了为检查到的key,基本上满足了所有要求。但是有时候就是那么的巧,既没有被定时器抽取到,又没有被使用,这些数据又如何从内存中消失?没关系,还有内存淘汰机制,当内存不够用时,内存淘汰机制就会上场。

内存淘汰机制
内存淘汰机制就保证了在redis的内存占用过多的时候,去进行内存淘汰,也就是删除一部分key,保证redis的内存占用率不会过高。

redis 提供 6种数据淘汰策略:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中随机移除key
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
noeviction:当内存不足以容纳新写入数据时,新写入操作会报错,无法写入新数据,一般不采用
4.0版本后增加以下两种:
volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key


1. 过期key处理

Redis之所以性能强,最主要的原因就是基于内存存储。然而单节点的Redis其内存大小不宜过大,会影响持久化或主从同步性能。我们可以通过修改配置文件来设置Redis的最大内存:
1653983341150.png
当内存使用达到上限时,就无法存储更多数据了。为了解决这个问题,Redis提供了一些策略实现内存回收:
内存过期策略
在学习Redis缓存的时候我们说过,可以通过expire命令给Redis的key设置TTL(存活时间):
1653983366243.png

可以发现,当key的TTL到期以后,再次访问name返回的是nil,说明这个key已经不存在了,对应的内存也得到释放。从而起到内存回收的目的。
Redis本身是一个典型的key-value内存存储数据库,因此所有的key、value都保存在之前学习过的Dict结构中。不过在其database结构体中,有两个Dict:一个用来记录key-value;另一个用来记录key-TTL。

这里有两个问题需要我们思考:Redis是如何知道一个key是否过期呢?
利用两个Dict分别记录key-value对及key-ttl对
是不是TTL到期就立即删除了呢?

1.1 惰性删除

惰性删除:顾明思议并不是在TTL到期后就立刻删除,而是在访问一个key的时候,检查该key的存活时间,如果已经过期才执行删除。
1653983652865.png

1.2 周期删除

周期删除:顾明思议是通过一个定时任务,周期性的抽样部分过期的key,然后执行删除。执行周期有两种:
Redis服务初始化函数initServer()中设置定时任务,按照server.hz的频率来执行过期key清理,模式为SLOW
Redis的每个事件循环前会调用beforeSleep()函数,执行过期key清理,模式为FAST

周期删除:顾明思议是通过一个定时任务,周期性的抽样部分过期的key,然后执行删除。执行周期有两种:
Redis服务初始化函数initServer()中设置定时任务,按照server.hz的频率来执行过期key清理,模式为SLOW
Redis的每个事件循环前会调用beforeSleep()函数,执行过期key清理,模式为FAST

SLOW模式规则:

  • 执行频率受server.hz影响,默认为10,即每秒执行10次,每个执行周期100ms。
  • 执行清理耗时不超过一次执行周期的25%.默认slow模式耗时不超过25ms
  • 逐个遍历db,逐个遍历db中的bucket,抽取20个key判断是否过期
  • 如果没达到时间上限(25ms)并且过期key比例大于10%,再进行一次抽样,否则结束
  • FAST模式规则(过期key比例小于10%不执行 ):
  • 执行频率受beforeSleep()调用频率影响,但两次FAST模式间隔不低于2ms
  • 执行清理耗时不超过1ms
  • 逐个遍历db,逐个遍历db中的bucket,抽取20个key判断是否过期
    如果没达到时间上限(1ms)并且过期key比例大于10%,再进行一次抽样,否则结束

小总结:
RedisKey的TTL记录方式:
在RedisDB中通过一个Dict记录每个Key的TTL时间
过期key的删除策略:
惰性清理:每次查找key时判断是否过期,如果过期则删除
定期清理:定期抽样部分key,判断是否过期,如果过期则删除。
定期清理的两种模式:
SLOW模式执行频率默认为10,每次不超过25ms
FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms

2. 内存淘汰策略

内存淘汰:就是当Redis内存使用达到设置的上限时,主动挑选部分key删除以释放更多内存的流程。Redis会在处理客户端命令的方法processCommand()中尝试做内存淘汰:1653983978671.png

淘汰策略
Redis支持8种不同策略来选择要删除的key:

  • noeviction: 不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略(不推荐使用)
  • volatile-ttl: 对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰
  • allkeys-random:对全体key ,随机进行淘汰。也就是直接从db->dict中随机挑选
  • volatile-random:对设置了TTL的key ,随机进行淘汰。也就是从db->expires中随机挑选。
  • allkeys-lru: 对全体key,基于LRU算法进行淘汰
  • volatile-lru: 对设置了TTL的key,基于LRU算法进行淘汰
  • allkeys-lfu: 对全体key,基于LFU算法进行淘汰
  • volatile-lfu: 对设置了TTL的key,基于LFI算法进行淘汰比较容易混淆的有两个:
    • LRU(Least Recently Used),最少最近使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。
    • LFU(Least Frequently Used),最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。

可以通过在Redis的配置文件中设置maxmemory-policy选项来选择合适的内存淘汰策略。

例如,将其设置为allkeys-lru:

maxmemory-policy allkeys-lru


在这里插入图片描述

相关文章:

  • Debezium日常分享系列之:Debezium 2.6.2.Final发布
  • Nginx(openresty) 查看连接数和并发送
  • 【SpringCloud学习笔记】Docker(上篇)
  • 提高篇(五):使用Processing创作互动艺术:从灵感到实现
  • QUIC 的多路径扩展
  • 【Vue3】理解toRef() 和 toRefs()
  • ChatGPT-4o体验demo
  • Docker面试整理-如何管理Docker容器的安全?
  • 【python报错】TypeError: dict.get() takes no keyword arguments
  • 【C++面向对象编程】(二)this指针和静态成员
  • 【Bug】httpClient循环调用除首次外会报Forbidden postman上用同样的参数可以
  • 微信小程序基础工作模板
  • 网络安全实战基础——实战工具与攻防环境介绍
  • 小阿轩yx-iptables 防火墙
  • JUC并发编程第十章——Java对象内存布局和对象头
  • ----------
  • 【Leetcode】104. 二叉树的最大深度
  • 【node学习】协程
  • Angular 4.x 动态创建组件
  • Angular数据绑定机制
  • CODING 缺陷管理功能正式开始公测
  • conda常用的命令
  • Date型的使用
  • ES学习笔记(12)--Symbol
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • Java到底能干嘛?
  • Map集合、散列表、红黑树介绍
  • opencv python Meanshift 和 Camshift
  • Python_网络编程
  • QQ浏览器x5内核的兼容性问题
  • spark本地环境的搭建到运行第一个spark程序
  • 测试开发系类之接口自动化测试
  • 基于HAProxy的高性能缓存服务器nuster
  • 排序算法学习笔记
  • 如何进阶一名有竞争力的程序员?
  • 如何利用MongoDB打造TOP榜小程序
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • ​数据链路层——流量控制可靠传输机制 ​
  • #Linux(帮助手册)
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (13)DroneCAN 适配器节点(一)
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (三)Kafka离线安装 - ZooKeeper开机自启
  • (三)SvelteKit教程:layout 文件
  • (一)、python程序--模拟电脑鼠走迷宫
  • (一)为什么要选择C++
  • (译)计算距离、方位和更多经纬度之间的点
  • (转)IOS中获取各种文件的目录路径的方法
  • .env.development、.env.production、.env.staging
  • .java 9 找不到符号_java找不到符号
  • .net dataexcel winform控件 更新 日志
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • .net 提取注释生成API文档 帮助文档
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .net程序集学习心得