Redis 缓存失效机制

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: Redis缓存失效的故事要从EXPIRE这个命令说起,EXPIRE允许用户为某个key指定超时时间,当超过这个时间之后key对应的值会被清除,这篇文章主要在分析Redis源码的基础上站在Redis设计者的角度去思考Redis缓存失效的相关问题。

Redis缓存失效的故事要从EXPIRE这个命令说起,EXPIRE允许用户为某个key指定超时时间,当超过这个时间之后key对应的值会被清除,这篇文章主要在分析Redis源码的基础上站在Redis设计者的角度去思考Redis缓存失效的相关问题。


Redis缓存失效机制


Redis缓存失效机制是为应对缓存应用的一种很常见的场景而设计的,讲个场景,在code学院中:


我们为了减轻后端数据库的压力,很开心的借助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本身的服务能力?


目录
相关文章
|
5月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
20天前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
111 1
Redis专题-实战篇二-商户查询缓存
|
5月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
737 0
|
21天前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。
|
6月前
|
缓存 并行计算 PyTorch
PyTorch CUDA内存管理优化:深度理解GPU资源分配与缓存机制
本文深入探讨了PyTorch中GPU内存管理的核心机制,特别是CUDA缓存分配器的作用与优化策略。文章分析了常见的“CUDA out of memory”问题及其成因,并通过实际案例(如Llama 1B模型训练)展示了内存分配模式。PyTorch的缓存分配器通过内存池化、延迟释放和碎片化优化等技术,显著提升了内存使用效率,减少了系统调用开销。此外,文章还介绍了高级优化方法,包括混合精度训练、梯度检查点技术及自定义内存分配器配置。这些策略有助于开发者在有限硬件资源下实现更高性能的深度学习模型训练与推理。
1033 0
|
5月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
195 32
|
5月前
|
缓存 NoSQL 算法
Redis数据库的键值过期和删除机制
我们需要注意的是,虽然Redis提供了这么多高级的缓存机制,但在使用过程中,必须理解应用的特性,选择合适的缓存策略,才能最大化Redis的性能。因此,在设计和实施应用程序时,理解应用的数据访问模式,以及这些模式如何与Redis的缓存机制相互作用,尤为重要。
192 24
|
5月前
|
缓存 NoSQL Java
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
114 5
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
|
存储 缓存 NoSQL
Spring Boot2.5 实战 MongoDB 与高并发 Redis 缓存|学习笔记
快速学习 Spring Boot2.5 实战 MongoDB 与高并发 Redis 缓存
Spring Boot2.5 实战 MongoDB 与高并发 Redis 缓存|学习笔记
|
缓存 NoSQL 安全
6.0Spring Boot 2.0实战 Redis 分布式缓存6.0|学习笔记
快速学习6.0Spring Boot 2.0实战 Redis 分布式缓存6.0。
470 0
6.0Spring Boot 2.0实战 Redis 分布式缓存6.0|学习笔记