嗨客网搜索
Redis过期策略及实现原理

描述

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

redis过期时间设置

语法

# 以秒为单位设置过期,这是最常用的方式 EXPIRE KEY time # 字符串独有的方式 SETEX KEY_NAME TIMEOUT VALUE

说明

除了 字符串 自己独有设置过期时间的方法外,其他方法都需要依靠 EXPIRE 方法来设置时间,如果没有设置时间,那缓存就是永不过期。

如果设置了过期时间,之后又想让缓存永不过期,使用 persist KEY

三种过期策略

定时删除

含义

在设置 KEY 的过期时间的同时,为该 KEY 创建一个定时器,让定时器在 KEY 的过期时间来临时,对 KEY 进行删除。

优点

该方法可以保证内存被尽快释放。

缺点

若过期 KEY 很多,删除这些 KEY 会占用很多的 CPU 时间,在 CPU 时间紧张的情况下,CPU 不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些 KEY。

定时器的创建耗时,若为每一个设置过期时间的 KEY 创建一个定时器(将会有大量的定时器产生),性能影响严重。

懒汉式删除

含义

KEY 过期的时候不删除,每次通过 KEY 获取值的时候去检查是否过期,若过期,则删除,返回 null。

优点

删除操作只发生在通过 KEY 取值的时候,而且只删除当前 KEY,所以对 CPU 时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的 KEY 了)。

缺点

若大量的 KEY 在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)。

定期删除

含义

每隔一段时间执行一次删除过期 KEY 操作。

优点

通过限制删除操作的时长和频率,来减少删除操作对 CPU 时间的占用。

缺点

在内存友好方面,不如 ”定时删除”(会造成一定的内存占用,但是没有懒汉式那么占用内存),在 CPU 时间友好方面,不如 ”懒汉式删除”(会定期的去进行比较和删除操作,cpu 方面不如懒汉式,但是比定时好)。

难点是合理设置删除操作的执行时长(每次删除执行多长时间)和执行频率(每隔多长时间做一次删除)(这个要根据服务器运行情况来定了),每次执行时间太长,或者执行频率太高对 cpu 都是一种压力。每次进行定期删除操作执行之后,需要记录遍历循环到了哪个标志位,以便下一次定期时间来时,从上次位置开始进行循环遍历。

说明

memcached 只是用了惰性删除,而 redis 同时使用了惰性删除与定期删除,这也是二者的一个不同点(可以看做是 redis 优于 memcached 的一点)。

对于懒汉式删除而言,并不是只有获取 KEY 的时候才会检查 KEY 是否过期,在某些设置 KEY 的方法上也会检查,比如 SETNX 命令,因为 SETNX 命令是在 KEY 不存在的情况下才设置,因为,如果不做过期 KEY 检查,那么直接设置,就会与我们原来的意思相违背。

定时任务

单线程的 redis,如何知道要运行定时任务?

redis 是单线程的,线程不但要处理定时任务,还要处理客户端请求,线程不能阻塞在定时任务或处理客户端请求上,那么,redis 是如何知道何时该运行定时任务的呢?

Redis 的定时任务会记录在一个称为最小堆的数据结构中。这个堆中,最快要执行的任务排在堆的最上方。在每个循环周期,Redis 都会将最小堆里面已经到点的任务立即进行处理。处理完毕后,将最快要执行的任务还需要的时间记录下来,这个时间就是接下来处理客户端请求的最大时长,若达到了该时长,则暂时不处理客户端请求而去运行定时任务。

配置

Redis 中定期删除使用的是统一的一个定时器,定时器执行的时长默认为 10,具体配置如下:

01_Redis过期策略.png

提高它的值将会占用更多的 cpu,当然相应的 redis 将会更快的处理同时到期的许多 key,以及更精确的去处理超时。 hz 的取值范围是 1~500,通常不建议超过 100,只有在请求延时非常低的情况下可以将值提升到 100。

Redis采用的过期策略

说明

Redis 采用的是懒汉式删除+定期删除。

懒汉式删除流程

  1. 在进行 GET 或 SETNX 等操作时,先检查 KEY 是否过期;
  2. 若过期,删除 KEY,然后执行相应操作;
  3. 若没过期,直接执行相应操作;

定期删除流程

简单而言,对指定 N 个库的每一个库随机删除小于等于指定 M 个过期 KEY,具体流程如下:

  1. 遍历每个数据库(就是 redis.conf 中配置的 ”database” 数量,默认为 16)
  2. 检查当前库中的指定个数个 KEY(默认是每个库检查 20 个 KEY,注意相当于该循环执行 20 次,循环体是下边的描述)
  3. 如果当前库中没有一个 KEY 设置了过期时间,直接执行下一个库的遍历随机
  4. 获取一个设置了过期时间的 KEY,检查该 KEY 是否过期,如果过期,删除 KEY 判断定期删除操作是否已经达到指定时长,若已经达到,直接退出定期删除。

对于定期删除,在程序中有一个全局变量 current_db 来记录下一个将要遍历的库,假设有 16 个库,我们这一次定期删除遍历了 10 个,那此时的 current_db 就是 11,下一次定期删除就从第 11 个库开始遍历,假设 current_db 等于 15 了,那么之后遍历就再从 0 号库开始(此时 current_db==0)。

嗨客网顶部