前言

我们都知道redis是常驻在内存当中的,因此他的效率比MySQL要快很多很多。但又引发了另外一个问题,内存从本质上讲,它是昂贵的,不能用于大量的长时间的存储,他是“不安全不稳定的“,并且有可能存在内存泄露,不能与磁盘相比。

那么如果解决这种问题呢?因此我们使用redis的时候,强制的应该给每个Key加上过期时间。我们来看看redis对过期的Key是怎么处理的。

过期键的判定

第一个问题,redis如何知道他是一个过期键呢?又该如何判定他过期了呢?

在数据库中, 所有键的过期时间都被保存在 redisDb 结构的 expires 字典里:

 typedef struct redisDb {

     // ...

     dict *expires;

     // ...

 } redisDb;

expires 字典的键是一个指向 dict 字典(键空间)里某个键的指针, 而字典的值则是键所指向的数据库键的到期时间, 这个值以 long long类型表示。

下图展示了一个含有三个键的数据库,其中 number 和 book 两个键带有过期时间

我们可以看到number和book是有一个过期时间的,他是long long类型。实则他是一个unix的时间戳,因此判断他是否过期就十分的简单了。

通过 expires 字典, 可以用以下步骤检查某个键是否过期:

  1. 检查键是否存在于 expires 字典:如果存在,那么取出键的过期时间;
  2. 检查当前 UNIX 时间戳是否大于键的过期时间:如果是的话,那么键已经过期;否则,键未过期。

可以用伪代码来描述这一过程:

 def is_expired(key):

     # 取出键的过期时间
key_expire_time = expires.get(key) # 如果过期时间不为空,并且当前时间戳大于过期时间,那么键已经过期
if expire_time is not None and current_timestamp() > key_expire_time:
return True # 否则,键未过期或没有设置过期时间
return False

过期键的清除

当我们知道这个键过期了,我们该如何清除呢?基本上有以下三种策略:

  • 定时删除:在设置键的过期时间时,创建一个定时事件,当过期时间到达时,由事件处理器自动执行键的删除操作。
  • 惰性删除:放任键过期不管,但是在每次从 dict 字典中取出键值时,要检查键是否过期,如果过期的话,就删除它,并返回空;如果没过期,就返回键值。
  • 定期删除:每隔一段时间,对 expires 字典进行检查,删除里面的过期键。

定时删除

定时删除策略对内存是最友好的: 因为它保证过期键会在第一时间被删除, 过期键所消耗的内存会立即被释放。

这种策略的缺点是, 它对 CPU 时间是最不友好的: 因为删除操作可能会占用大量的 CPU 时间 —— 在内存不紧张、但是 CPU 时间非常紧张的时候 (比如说,进行交集计算或排序的时候), 将 CPU 时间花在删除那些和当前任务无关的过期键上, 这种做法毫无疑问会是低效的。

除此之外, 目前 Redis 事件处理器对时间事件的实现方式 —— 无序链表, 查找一个时间复杂度为 O(N) —— 并不适合用来处理大量时间事件。

惰性删除

惰性删除对 CPU 时间来说是最友好的: 它只会在取出键时进行检查, 这可以保证删除操作只会在非做不可的情况下进行 —— 并且删除的目标仅限于当前处理的键, 这个策略不会在删除其他无关的过期键上花费任何 CPU 时间。

惰性删除的缺点是, 它对内存是最不友好的: 如果一个键已经过期, 而这个键又仍然保留在数据库中, 那么 dict 字典和 expires 字典都需要继续保存这个键的信息, 只要这个过期键不被删除, 它占用的内存就不会被释放。

在使用惰性删除策略时, 如果数据库中有非常多的过期键, 但这些过期键又正好没有被访问的话, 那么它们就永远也不会被删除(除非用户手动执行), 这对于性能非常依赖于内存大小的 Redis 来说, 肯定不是一个好消息。

举个例子, 对于一些按时间点来更新的数据, 比如日志(log), 在某个时间点之后, 对它们的访问就会大大减少, 如果大量的这些过期数据积压在数据库里面, 用户以为它们已经过期了(已经被删除了), 但实际上这些键却没有真正的被删除(内存也没有被释放), 那结果肯定是非常糟糕。

定期删除

从上面对定时删除和惰性删除的讨论来看, 这两种删除方式在单一使用时都有明显的缺陷: 定时删除占用太多 CPU 时间, 惰性删除浪费太多内存。

定期删除是这两种策略的一种折中:

  • 它每隔一段时间执行一次删除操作,并通过限制删除操作执行的时长和频率,籍此来减少删除操作对 CPU 时间的影响。
  • 另一方面,通过定期删除过期键,它有效地减少了因惰性删除而带来的内存浪费。

因此最终redis使用的过期键删除策略是惰性删除加上定期删除, 这两个策略相互配合,可以很好地在合理利用 CPU 时间和节约内存空间之间取得平衡。

因此redis大致流程如下:获取key之前,会检查key是否过期,如过期,直接删除,返回null。

并且会定期的随机的检查大约25%的key是否过期,如果超过一定比例的key被过期。那么继续循环,直至低于这个数值。

这个定期的时间,以及数值都可以在conf文件里面配置。

redis系列之------过期策略的更多相关文章

  1. Redis 内存溢出过期策略

    1: 设置内存最大值, 如果该主机只作为 redis 服务器, 无其它比较占用资源的服务, 建议设置为内存的 3/4 大小, 单位 B 2: 设置内存溢出解决策略, 推荐 1-5 任选一种, 不推荐 ...

  2. NoSql数据库Redis系列(6)——Redis数据过期策略详解

    本文对Redis的过期机制简单的讲解一下 讲解之前我们先抛出一个问题,我们知道很多时候服务器经常会用到redis作为缓存,有很多数据都是临时缓存一下,可能用过之后很久都不会再用到了(比如暂存sessi ...

  3. Redis过期策略

    一.设置过期时间 expire key time(以秒为单位) -- 这是最常用的方式 setex(String key, int seconds, String value) -- 字符串独有的方式 ...

  4. Redis数据过期策略详解

    http://www.cnblogs.com/xuliangxing/p/7151812.html 本文对Redis的过期机制简单的讲解一下 讲解之前我们先抛出一个问题,我们知道很多时候服务器经常会用 ...

  5. Redis的过期策略和内存淘汰策略(转)

    Redis的过期策略 我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间.Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理. ...

  6. Redis学习笔记--Redis数据过期策略详解

    本文对Redis的过期机制简单的讲解一下 讲解之前我们先抛出一个问题,我们知道很多时候服务器经常会用到redis作为缓存,有很多数据都是临时缓存一下,可能用过之后很久都不会再用到了(比如暂存sessi ...

  7. Redis学习笔记--Redis数据过期策略详解==转

    本文对Redis的过期机制简单的讲解一下 讲解之前我们先抛出一个问题,我们知道很多时候服务器经常会用到redis作为缓存,有很多数据都是临时缓存一下,可能用过之后很久都不会再用到了(比如暂存sessi ...

  8. 了解Redis过期策略及实现原理

    我们在使用redis时,一般会设置一个过期时间,当然也有不设置过期时间的,也就是永久不过期. 当我们设置了过期时间,redis是如何判断是否过期,以及根据什么策略来进行删除的. redis设置过期时间 ...

  9. Redis过期策略(转)

    1.设置过期时间 expire key time(以秒为单位)--这是最常用的方式 setex(String key, int seconds, String value)--字符串独有的方式 具体的 ...

随机推荐

  1. 从二叉搜索树到AVL树再到红黑树 B树

    这几种树都属于数据结构中较为复杂的,在平时面试中,经常会问理解用法,但一般不会问具体的实现,所以今天来梳理一下这几种树之间的区别与联系,感谢知乎用户@Cailiang,这篇文章参考了他的专栏. 二叉查 ...

  2. 被这个C程序折腾死了

    The C programming language 的第13页,1.5.3 行计数的那里,那个统计换行符个数的程序我好像无法运行,无论输入什么,按多少下enter,什么都出不来. #include& ...

  3. 五、RabbitMQ Java Client基本使用详解

    Java Client的5.x版本系列需要JDK 8,用于编译和运行.在Android上,仅支持Android 7.0或更高版本.4.x版本系列支持7.0之前的JDK 6和Android版本. 加入R ...

  4. JVM组成与作用

    class loader 类加载器:加载类文件到内存.Class loader只管加载,只要符合文件结构就加载,至于能否运行,它不负责,那是有Exectution Engine 负责的.exectio ...

  5. 3DMAX 卸载工具,完美彻底卸载清除干净3dmax各种残留注册表和文件

    一些同学安装3dmax出错了,也有时候想重新安装3dmax的时候会出现这种本电脑windows系统已安装3dmax,你要是不留意直接安装,只会安装3dmax的附件,3dmax是不会安装上的.这种原因呢 ...

  6. 防止跨站攻击——CSRFToken

    怎么防止跨站攻击: 表单:在 Form 表单中添加一个隐藏的的字段,值是 csrf_token. 非表单:在ajax获取数据时,添加headers:{ 'X-CSRFToken':getCookie( ...

  7. 【阅读笔记】rocketmq 概念与架构 (一)

    介绍 rocketmq 框架与基本概念 1. 概念 1.1 namesrv(name server) 记录了 broker 集群信息,消息队列的信息以及 key-value 配置,见 RouteInf ...

  8. byte的取值范围是-128~127,那么包含-128和127吗?

    本帖最后由 王德升老师 于 2019-12-27 17:56 编辑 byte的取值范围为什么是-128~127?如果面试官问你取值范围包含127吗?1. 首先我们知道Java中byte类型是1个字节占 ...

  9. 用数组实现队列(C++)

    #include <iostream> using namespace std; template<typename T, int size = 0> class Queue ...

  10. IOUtils和FileUtils的学习笔记

    pom文件导入依赖import org.apache.commons.io.FileUtils; <dependency> <groupId>commons-io</gr ...