Redis缓存失效机制
Redis缓存失效的故事要从EXPIRE这个命令说起,EXPIRE允许用户为某个key指定超时时间,当超过这个时间之后key对应的值会被清除,这篇文章主要在分析Redis源码的基础上站在Redis设计者的角度去思考Redis缓存失效的相关问题。
Redis缓存失效机制
Redis缓存失效机制是为应对缓存应用的一种很常见的场景而设计的,讲个场景:
我们为了减轻后端数据库的压力,很开心的借助Redis服务把变化频率不是很高的数据从DB load出来放入了缓存,因此之后的一段时间内我们都可以直接从缓存上拿数据,然而我们又希望一段时间之后,我们再重新的从DB load出当前的数据放入缓存,这个事情怎么做呢?
问题提出来了,这个问题怎么解决呢?好吧,我们对于手头的语言工具很熟悉,坚信可以很快的写出这么一段逻辑:我们记录上次从db load数据的时间,然后每次响应服务的时候都去判断时间是不是过期了,要不要从db重新load了……。当然这种方法也是可以的,然而当我们查阅Redis command document的时候,发现我们做了本来不需要做的事情,Redis本身提供这种机制,我们只要借助EXPIRE命令就可以轻松的搞定这件事情:
EXPIRE key 30
上面的命令即为key设置30秒的过期时间,超过这个时间,我们应该就访问不到这个值了,到此为止我们大概明白了什么是缓存失效机制以及缓存失效机制的一些应用场景,接下来我们继续深入探究这个问题,Redis缓存失效机制是如何实现的呢?
延迟失效机制
延迟失效机制即当客户端请求操作某个key的时候,Redis会对客户端请求操作的key进行有效期检查,如果key过期才进行相应的处理,延迟失效机制也叫消极失效机制。我们看看t_string组件下面对get请求处理的服务端端执行堆栈:
getCommand -> getGenericCommand -> lookupKeyReadOrReply -> lookupKeyRead -> expireIfNeeded
关键的地方是expireIfNeed,Redis对key的get操作之前会判断key关联的值是否失效,这里先插入一个小插曲,我们看看Redis中实际存储值的地方是什么样子的:
typedef struct redisDb { dict *dict; /* The keyspace for this DB */ dict *expires; /* Timeout of keys with a timeout set */ dict *blocking_keys; /* Keys with clients waiting for data (BLPOP) */ dict *ready_keys; /* Blocked keys that received a PUSH */ dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */ int id; long long avg_ttl; /* Average TTL, just for stats */ } redisDb;
上面是Redis中定义的一个结构体,dict是一个Redis实现的一个字典,也就是每个DB会包括上面的五个字段,我们这里只关心两个字典,一个是dict,一个是expires:
dict是用来存储正常数据的,比如我们执行了set key "hahaha",这个数据就存储在dict中。
expires使用来存储关联了过期时间的key的,比如我们在上面的基础之上有执行的expire key 1,这个时候就会在expires中添加一条记录。
回过头来看看expireIfNeeded的流程,大致如下:
从expires中查找key的过期时间,如果不存在说明对应key没有设置过期时间,直接返回。
如果是slave机器,则直接返回,因为Redis为了保证数据一致性且实现简单,将缓存失效的主动权交给Master机器,slave机器没有权限将key失效。
如果当前是Master机器,且key过期,则master会做两件重要的事情:1)将删除命令写入AOF文件。2)通知Slave当前key失效,可以删除了。
master从本地的字典中将key对于的值删除。
主动失效机制
主动失效机制也叫积极失效机制,即服务端定时的去检查失效的缓存,如果失效则进行相应的操作。
我们都知道Redis是单线程的,基于事件驱动的,Redis中有个EventLoop,EventLoop负责对两类事件进行处理:
一类是IO事件,这类事件是从底层的多路复用器分离出来的。
一类是定时事件,这类事件主要用来事件对某个任务的定时执行。
看起来Redis的EventLoop和Netty以及JavaScript的EventLoop功能设计的大概类似,一方面对网络I/O事件处理,一方面还可以做一些小任务。
为什么讲到Redis的单线程模型,因为Redis的主动失效机制逻辑是被当做一个定时任务来由主线程执行的,相关代码如下:
if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) { redisPanic("Can't create the serverCron time event."); exit(1); }
serverCron就是这个定时任务的函数指针,adCreateTimeEvent将serverCron任务注册到EventLoop上面,并设置初始的执行时间是1毫秒之后。接下来,我们想知道的东西都在serverCron里面了。serverCron做的事情有点多,我们只关心和本篇内容相关的部分,也就是缓存失效是怎么实现的,我认为看代码做什么事情,调用堆栈还是比较直观的:
aeProcessEvents ->processTimeEvents ->serverCron -> databasesCron -> activeExpireCycle -> activeExpireCycleTryExpire
EventLoop通过对定时任务的处理,触发对serverCron逻辑的执行,最终之执行key过期处理的逻辑,值得一提的是,activeExpireCycle逻辑只能由master来做。
遗留问题
Redis对缓存失效的处理机制大概分为两种,一种是客户端访问key的时候消极的处理,一种是主线程定期的积极地去执行缓存失效清理逻辑,上面文章对于一些细节还没有展开介绍,但是对于Redis缓存失效实现机制这个话题,本文留下几个问题:
Redis缓存失效逻辑为什么只有master才能操作?
上面提到如果客户端访问的是slave,slave并不会清理失效缓存,那么这次客户端岂不是获取了失效的缓存?
上面介绍的两种缓存失效机制各有什么优缺点?Redis设计者为什么这么设计?
服务端对客户端的请求处理是单线程的,单线程又要去处理失效的缓存,是不是会影响Redis本身的服务能力?
作者:Float_Luuu

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Zabbix 监控部署
zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。 zabbix能监视各种网络参数,保证服务器系统的安全运营;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 zabbix由2部分构成,zabbix server与可选组件zabbix agent。 zabbix server可以通过SNMP,zabbix agent,ping,端口监视等方法提供对远程服务器/网络状态的监视,数据收集等功能,它可以运行在Linux, Solaris, HP-UX, AIX, Free BSD, Open BSD, OS X等平台上。 zabbix agent需要安装在被监视的目标服务器上,它主要完成对硬件信息或与操作系统有关的内存,CPU等信息的收集。zabbix agent可以运行在Linux,Solaris,HP-UX,AIX,Free BSD,Open BSD, OS X, Tru64/OSF1, Windows等系统之上。 zabbix server可以单独监视远程服务器的服务状态;同时也可以与zabbix agent配合,可以轮询zab...
- 下一篇
使用Mockito修改Bean的依赖
概述 在使用单元测试时经常会遇到某些dependency依赖了外部资源,或者想主动绕过真正的方法执行mock返回结果而快速得到单元测试最终的期望结果,可能有以下两种场景, 对于TestCase A,设单元测试的方法是Service A的execute1方法和execute2方法,在执行execute1和execute2方法时都会调用ServiceB的不同方法,即ServiceA依赖了ServiceB;一个场景是完全对ServiceB进行Mock,如单元测试ServiceA#execute1方法时都通过Mock返回结果;一个场景是部分ServiceB的方法执行真实的业务逻辑(如查询数据库),一部分方法执行Mock返回结果,或Spy,如如单元测试ServiceA#execute2方法时,只mock ServiceB#b2结果,真正执行ServiceB#b1方法。 对TestCase的Service的依赖Bean的完全Mock 当对ServiceA的方法执行单元测试时,如ServiceA -> ServiceB,此时对ServiceB进行Mock,然后将其设置到ServiceA的属性中...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- 设置Eclipse缩进为4个空格,增强代码规范
- SpringBoot2整合Redis,开启缓存,提高访问速度