redis系列之------过时策略

前言

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

那么若是解决这种问题呢?所以咱们使用redis的时候,强制的应该给每一个Key加上过时时间。咱们来看看redis对过时的Key是怎么处理的。数据库

过时键的断定

第一个问题,redis如何知道他是一个过时键呢?又该如何断定他过时了呢?安全

在数据库中, 全部键的过时时间都被保存在 redisDb 结构的 expires 字典里:性能

1 typedef struct redisDb {
2 
3     // ...
4 
5     dict *expires;
6 
7     // ...
8 
9 } redisDb;

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

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

 

咱们能够看到number和book是有一个过时时间的,他是long long类型。实则他是一个unix的时间戳,所以判断他是否过时就十分的简单了。指针

经过 expires 字典, 能够用如下步骤检查某个键是否过时:日志

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

能够用伪代码来描述这一过程:code

 1 def is_expired(key):
 2 
 3     # 取出键的过时时间
 4     key_expire_time = expires.get(key)
 5 
 6     # 若是过时时间不为空,而且当前时间戳大于过时时间,那么键已通过期
 7     if expire_time is not None and current_timestamp() > key_expire_time:
 8         return True
 9 
10     # 不然,键未过时或没有设置过时时间
11     return False

过时键的清除

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

  • 定时删除:在设置键的过时时间时,建立一个定时事件,当过时时间到达时,由事件处理器自动执行键的删除操做。
  • 惰性删除:听任键过时无论,可是在每次从 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文件里面配置。

相关文章
相关标签/搜索