草庐IT

Redis过期策略详解

向着百万年薪努力的小赵 2023-04-11 原文

为什么要有过期策略?

因为我们的redis是一个内存型数据库,我们的数据都是放在内存里面的!但是内存是有大小的!
比如,redis有个很重要的配置文件,redis.conf,里面有个配置

# maxmemory <bytes> //redis占用的最大内存

如果我们不淘汰,那么它的数据就会满,满了肯定就不能再放数据,发挥不了redis的作用!
比如冰箱,你如果放满了,那么你的菜就不能放冰箱了!
过期策略:拿出redis中已经过期了的数据,就像你从冰箱把坏的菜拿出来!!但是有一种情况,就是冰箱里面的菜都没坏,redis里面的数据都没过期,它也是会放满的,那怎么办?

那么当redis里面的数据都没过期。但是内存满了的时候,我们就得从未过期的数据里面去拿出一些扔掉,那么这个就是我们的淘汰策略,详见另一篇文章:Redis的淘汰策略详解

Redis自带的有两种过期策略,我们也可以自己实现一些过期的策略,不过今天主要研究自带的

惰性过期(被动过期)

这个怎么实现的呢?所谓惰性,是不是就很懒的意思,就是只有访问我的时候,我才会去判断过不过期,不然我懒得去判断,我不会主动去判断过没过期
访问一个key时判断该 key 是否已过期,过期则清除
该策略就可以最大化地节省CPU资源,因为它平时都懒得去判断,所以也没有啥cpu损耗,因为只有访问的时候我才去判断一下!
但是却对内存非常不友好。因为你不实时过期了,该过期删除的就可能一直堆积在内存里面!极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
源码(expireIfNeeded db.c文件下1302行):

int expireIfNeeded(redisDb *db, robj *key) {
	if (!keyIsExpired(db,key)) return 0;
	/* If we are running in the context of a slave,instead of
	 * evicting the expired key from the database, we return ASAP:
	 * the slave key expiration is controlled by the master that will
	 * send us synthesized DEL operations for expired keys.
	 *
	 * Still we try to return the right information to the caller,
	 * that is, 0 if we think the key should be still valid, 1 if
	 * we think the key is expired at this time. */
	 if (server.masterhost != NULL) return 1;
	 /* Delete the key */
	 server.stat_expiredkeys++;
	 propagateExpire(db,key,server.lazyfree_lazy_expire);
	 notifyKeyspaceEvent(NOTIFY_EXPIRED,"expired",key,db->id);
	 int retval = server.lazyfree_lazy_expire ?
	 dbAsyncDelete(db,key):
	 dbSyncDelete(db,key);
	 if (retval) signalModifiedKey(NULL,db,key);
	 return retval;
}

我们刚才说了主动过期因为太耗CPU它不用
但是惰性这种又会可能导致大量无效数据堆积在内存里面,我们总得有个办法来解决吧!不能让他一直堆在内存里面啊!
所以我们就有了一个定期过期策略,虽然实时性比不上定时的,但是也足够解决垃圾数据大量堆积在内存的这种情况!

定期过期

所谓定期过期,就是每过一段时间去执行一次删除过期key。
这里需要先学习下redis的一个数据结构:字典 必须学哦
Redis数据结构——dict(字典)
大概的结构如图:

redis的hash默认使用的是ht[0],ht[1]不会初始化和分配空间。
哈希表dictht是用链地址法来解决碰撞问题的。如果节点数量比哈希表的大小要大很多的话,那么哈希表就会退化成多个链表,哈希表本身的性能优势就不再存在,在这种情况下需要扩容。
Redis里面的这种操作叫做rehash。 那么它怎么做rehash的,也是看上面字典这篇文章

我们来看定期过期到底是怎么实现的:
先想一下,如果让我们实现一个定期删除,应该怎么做?
我想到的是定期去循环找过期的key,然后去删掉!巧的是Redis也是这样做的
那么问题又来了:

  1. 我们去循环谁?是不是所有的key
  2. 我们多久循环一次?

第一个问题,我们并不是去循环所有的key,因为Redis里经常会存放巨多的数据,对我们需要经常清理,全部遍历一遍显然不现实,而Redis采取的是取样这个操作
具体实现方式为:

  1. 不是一次性把所有设置了过期时间的数据拿出来,而是按hash桶维度取 里面取值,取到20个值为止,如果第一个有30个,那么也会取30个! 如果一直取不到20,那么最多400个桶
  2. 删除取出值的过期key
  3. 如果400个桶都取不到值,或者取出的key 删除的比例大于10%,继续上 面的操作
  4. 每循环16次会去检测时间,超过指定时间就跳出

ps:按hash桶维度取key的逻辑是:最后一个桶会取完桶内所有的key,不论里面有多少个,每取完一个桶判断一下是否取到了20个,最多取400个桶

现在我们第一个问题解决了!那么第二个问题,定期定期,那么多久去做上面那件时间!那么redis里面有个很重要的概念叫做时间事件,那么这个时间事件是什么意思了,就是定时去做一些事情,那么redis里面有个方法叫serverCron(),在文件server.c中;就是它的时间事件去调用的清理
它里面干了很多事情,比如:

  1. 更新服务器的各类统计信息,比如时间、内存占用、数据库占用情况等
  2. 清理数据库中的过期键值对。
  3. 关闭和清理连接失效的客户端
  4. 尝试进行持久化操作

那么这个时间事件多久去执行一次呢,其实是由你们自己决定的!
redis.conf 中通过 hz 配置,hz代表的意思是每秒执行多少次!默认10次,也就是100ms我们就会去执行定期过期!!

定期过期的逻辑,简单画图

怎么样,学会了Redis的过期策略了吧,还不一键三连

有关Redis过期策略详解的更多相关文章

  1. ruby-on-rails - 覆盖 Controller 中的 protect_from_forgery 策略 - 2

    我想使用两种不同的protect_from_forgery策略构建一个Rails应用程序:一种用于Web应用程序,一种用于API。在我的应用程序Controller中,我有这行代码:protect_from_forgerywith::exception为了防止CSRF攻击,它工作得很好。在我的API命名空间中,我创建了一个继承self的应用程序Controller的api_controller,它是API命名空间中所有其他Controller的父类,我将上面的代码更改为:protect_from_forgery:null_session.遗憾的是,我在尝试发出POST请求时遇到错误:“

  2. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  3. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  4. ruby - 执行过期异常使 Ruby 线程崩溃,但处理了 Timeout::Error - 2

    任何人都可以解释为什么当对方法的调用看起来像这样时我可能会看到这个堆栈(由HTTParty::post请求引起):beginresponse=HTTParty::post(url,options)rescuelogger.warn("Couldnotpostto#{url}")rescueTimeout::Errorlogger.warn("Couldnotpostto#{url}:timeout")end堆栈:/usr/local/lib/ruby/1.8/timeout.rb:64:in`timeout'/usr/local/lib/ruby/1.8/net/protocol.rb

  5. 【详解】Docker安装Elasticsearch7.16.1集群 - 2

    开门见山|拉取镜像dockerpullelasticsearch:7.16.1|配置存放的目录#存放配置文件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/config#存放数据的文件夹mkdir-p/opt/docker/elasticsearch/node-1/data#存放运行日志的文件夹mkdir-p/opt/docker/elasticsearch/node-1/log#存放IK分词插件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/plugins若你使用了moba,直接右键新建即可如上图所示依次类推创建

  6. 【Elasticsearch基础】Elasticsearch索引、文档以及映射操作详解 - 2

    文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就

  7. 最强Http缓存策略之强缓存和协商缓存的详解与应用实例 - 2

    HTTP缓存是指浏览器或者代理服务器将已经请求过的资源保存到本地,以便下次请求时能够直接从缓存中获取资源,从而减少网络请求次数,提高网页的加载速度和用户体验。缓存分为强缓存和协商缓存两种模式。一.强缓存强缓存是指浏览器直接从本地缓存中获取资源,而不需要向web服务器发出网络请求。这是因为浏览器在第一次请求资源时,服务器会在响应头中添加相关缓存的响应头,以表明该资源的缓存策略。常见的强缓存响应头如下所述:Cache-ControlCache-Control响应头是用于控制强制缓存和协商缓存的缓存策略。该响应头中的指令如下:max-age:指定该资源在本地缓存的最长有效时间,以秒为单位。例如:Ca

  8. IDEA 2022 创建 Spring Boot 项目详解 - 2

    如何用IDEA2022创建并初始化一个SpringBoot项目?目录如何用IDEA2022创建并初始化一个SpringBoot项目?0. 环境说明1.  创建SpringBoot项目 2.编写初始化代码0. 环境说明IDEA2022.3.1JDK1.8SpringBoot1.  创建SpringBoot项目        打开IDEA,选择NewProject创建项目。        填写项目名称、项目构建方式、jdk版本,按需要修改项目文件路径等信息。        选择springboot版本以及需要的包,此处只选择了springweb。        此处需特别注意,若你使用的是jdk1

  9. ruby - 使用 Ruby Net 实现重新连接策略 - 2

    我正在开发一个将XML发布到某些网络服务的小型应用程序。这是使用Net::HTTP::Post::Post完成的。但是,服务提供商建议使用重新连接。类似于:第一个请求失败->2秒后重试第二个请求失败->5秒后重试第三次请求失败->10秒后重试...这样做的好方法是什么?简单地在循环中运行以下代码,捕获异常并在一定时间后再次运行?或者还有其他聪明的方法吗?也许Net包甚至有一些我不知道的内置功能?url=URI.parse("http://some.host")request=Net::HTTP::Post.new(url.path)request.body=xmlrequest.con

  10. 详解Unity中的粒子系统Particle System (二) - 2

    前言上一篇我们简要讲述了粒子系统是什么,如何添加,以及基本模块的介绍,以及对于曲线和颜色编辑器的讲解。从本篇开始,我们将按照模块结构讲解下去,本篇主要讲粒子系统的主模块,该模块主要是控制粒子的初始状态和全局属性的,以下是关于该模块的介绍,请大家指正。目录前言本系列提要一、粒子系统主模块1.阅读前注意事项2.参考图3.参数讲解DurationLoopingPrewarmStartDelayStartLifetimeStartSpeed3DStartSizeStartSize3DStartRotationStartRotationFlipRotationStartColorGravityModif

随机推荐