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:

  1. dict是用来存储正常数据的,比如我们执行了set key “hahaha”,这个数据就存储在dict中。

  2. expires使用来存储关联了过期时间的key的,比如我们在上面的基础之上有执行的expire key 1,这个时候就会在expires中添加一条记录。

回过头来看看expireIfNeeded的流程,大致如下:

  1. 从expires中查找key的过期时间,如果不存在说明对应key没有设置过期时间,直接返回。

  2. 如果是slave机器,则直接返回,因为Redis为了保证数据一致性且实现简单,将缓存失效的主动权交给Master机器,slave机器没有权限将key失效。

  3. 如果当前是Master机器,且key过期,则master会做两件重要的事情:1)将删除命令写入AOF文件。2)通知Slave当前key失效,可以删除了。

  4. master从本地的字典中将key对于的值删除。

主动失效机制

主动失效机制也叫积极失效机制,即服务端定时的去检查失效的缓存,如果失效则进行相应的操作。

我们都知道Redis是单线程的,基于事件驱动的,Redis中有个EventLoop,EventLoop负责对两类事件进行处理:

  1. 一类是IO事件,这类事件是从底层的多路复用器分离出来的。

  2. 一类是定时事件,这类事件主要用来事件对某个任务的定时执行。

看起来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缓存失效实现机制这个话题,本文留下几个问题:

  1. Redis缓存失效逻辑为什么只有master才能操作?

  2. 上面提到如果客户端访问的是slave,slave并不会清理失效缓存,那么这次客户端岂不是获取了失效的缓存?

  3. 上面介绍的两种缓存失效机制各有什么优缺点?Redis设计者为什么这么设计?

  4. 服务端对客户端的请求处理是单线程的,单线程又要去处理失效的缓存,是不是会影响Redis本身的服务能力?

Redis 缓存失效机制的更多相关文章

  1. Redis 缓存失效和回收机制续

    二.Redis Key失效机制 Redis的Key失效机制,主要借助借助EXPIRE命令: EXPIRE key 30 上面的命令即为key设置30秒的过期时间,超过这个时间,我们应该就访问不到这个值 ...

  2. redis缓存处理机制

    1.redis缓存处理机制:先从缓存里面取,取不到去数据库里面取,然后丢入缓存中 例如:系统参数处理工具类 package com.ztesoft.iotcmp.utils; import com.e ...

  3. SpringBoot缓存管理(三) 自定义Redis缓存序列化机制

    前言 在上一篇文章中,我们完成了SpringBoot整合Redis进行数据缓存管理的工作,但缓存管理的实体类数据使用的是JDK序列化方式(如下图所示),不便于使用可视化管理工具进行查看和管理. 接下来 ...

  4. Redis 缓存失效和回收机制

    本文及后续文章,Redis版本均是v3.2.8 一.内存回收策略 maxmemory配置用于配置Redis存储数据时指定限制的内存大小.我们可以通过redis.conf配置或者使用CONFIG SET ...

  5. redis,缓存雪崩,粗粒度锁,缓存一致性

    1, redis单线程为什么快 io多路复用技术 单线程避免多线程的频繁切换问题 memcache缺点 kv形式数据 没有持久化mongodb 海量数据的访问效率 mr的计算模型文档就是类似json的 ...

  6. [转]高并发访问下避免对象缓存失效引发Dogpile效应

    避免Redis/Memcached缓存失效引发Dogpile效应 Redis/Memcached高并发访问下的缓存失效时可能产生Dogpile效应(Cache Stampede效应). 推荐阅读:高并 ...

  7. Redis缓存如何保证一致性

    为什么使用Redis做缓存 MySQL缺点 单机连接数目有限 对数据进行写速度慢 Redis优点 内存操作数据速度快 IO复用,速度快 单线程模型,避免线程切换带来的开销,速度快 一致性问题 读数据的 ...

  8. Redis的缓存策略和主键失效机制

    作为缓存系统都要定期清理无效数据,就需要一个主键失效和淘汰策略. >>EXPIRE主键失效机制 在Redis当中,有生存期的key被称为volatile,在创建缓存时,要为给定的key设置 ...

  9. Redis 利用锁机制来防止缓存过期产生的惊群现象-转载自 http://my.oschina.net/u/1156660/blog/360552

    首先,所谓的缓存过期引起的“惊群”现象是指,在大并发情况下,我们通常会用缓存来给数据库分压,但是会有这么一种情况发生,那就是在一定时间 内生成大量的缓存,然后当缓存到期之后又有大量的缓存失效,导致后端 ...

随机推荐

  1. linux服务器通过X11实现图形化界面显示

    1 背景描述 有些LINUX服务器出于性能和效率的考虑,通常都是没有安装图形化界面的,那么图形化程序在服务器上压根儿就跑不起来,或者无法直接显示出来,这就很尴尬了!那么如何解决这个问题呢?可以基于X1 ...

  2. springboot热部署JRebel插件

    激活参考:https://www.52pojie.cn/thread-906163-1-1.html 最后设置为离线,不然每次启动要重新激活 环境idea是2018.1.1版本 JRebel是最新版2 ...

  3. 「LOJ2091」「ZJOI2016」小星星 容斥+DP

    题目描述 小 Y 是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品.她有\(n\)颗小星星,用 \(m\)条彩色的细线串了起来,每条细线连着两颗小星星.有一天她发现,她的饰品被破坏了,很多细线都被拆掉 ...

  4. 常见的几种异常类型 Exception

    常见异常类型:Java中的异常分为两大类: 1.Checked Exception(非Runtime Exception) 2.Unchecked Exception(Runtime Exceptio ...

  5. [转帖]Helm V2 迁移到 V3 版本

    Helm V2 迁移到 V3 版本 -- :: Mr-Liuqx 阅读数 63更多 分类专栏: kubernetes 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上 ...

  6. Qt 5.12 LTS 部署

    1. 拷贝release生成的exe到一个独立的目录deploy 2. windeployqt.exe A_Toolkit.exe 3. 将qt\qt5.12.5\tool\mingw730_64\b ...

  7. Wing电信平台操作方法

    Wing电信平台操作文档 当前文档编制于2019/9/3 一.登陆 登陆网址 https://www.ctwing.cn/ 点击右上角控制台 点击左侧栏点击产品中心 选择需要注册的产品 二.注册设备 ...

  8. "CreateProcess error=206, 文件名或扩展名太长。",用gradle构建项目创建mapper文件时提示这个错误,是Windows Gradle长类路径问题,官方已经修复

    用gradle构建项目mapper文件时,提示这个错误,这个是Windows Gradle长类路径问题, gradle官方已经解决了这个问题. 官网给出的解决方法地址:https://plugins. ...

  9. Codeforces Round #569 Div. 1

    A:n-1次操作后最大值会被放到第一个,于是暴力模拟前n-1次,之后显然是循环的. #include<bits/stdc++.h> using namespace std; #define ...

  10. 单实例dg软件从10.2.0.4版本安装至10.2.0.5.12

    DG环境搭建需求,因此安装与主库相同的软件版本 1.主库软件版本10.2.0.5.12 2dg环境提供的是全新的10.2.0.4.0 3.安装步骤,安装10.2.0.5 静默安装 psu安装10.2. ...