通过EXPIRE,PEXPIRE,SETEX等指令,Redis可以为数据库中的某键设置生存时间,在经过指定时间之后,服务器就会删除生存时间为0的键。
设置过期时间
保存过期时间
redisDb结构的expires字典保存了数据库中所有键的过期时间。
1 | typedef struct redisDb { |
过期字典的键是一个指针指向键空间中的某一个键对象,值是一个long long类型的整数,保存了毫秒精度的UNIX时间戳。当执行设置过期时间的命令时,服务器会在数据库的过期字典中关联给定的数据库键以及过期时间。
移除过期时间
PERSIST命令可以移除一个键的过期时间。当需要移除过期时间时,会从expires字典之中移除给定键的键值对关联。
返回过期时间
TTL和PTTL命令可以返回秒或毫秒为单位的剩余生存时间。其都是通过计算两个键的过期时间与当前时间之差来实现的。
过期键删除策略
可能的删除策略
定时删除
通过在设置键的同时,创建一个定时器。在定时器结束时,立即删除该键。该策略对内存友好,可以保证过期键最快的被删除。但是在比较多过期键的情况下,会很占用CPU资源,导致服务器的响应时间降低。
惰性删除
惰性删除指的是当从键空间中获取值时,检查该键是否过期,如果过期的话,则删除该键,否则返回该键。惰性删除策略不会在删除其他无关键上花费CPU时间,属于CPU友好型策略。但是如果一个键已经过期但是一致没有被访问到,那么该键永远不会过期,所占用的内存永远不会释放。
定期删除
每隔一段时间,对数据库进行检查,删除其中的过期键。其难点在于确定执行的时长与频率。如果执行的太频繁,会导致占用过多CPU,如果执行的太少,会导致内存浪费的情况。
Redis中的删除策略实现
Redis中整合了惰性删除和定期删除两种策略,取得了合理使用CPU时间和内存空间之间的平衡。
惰性删除实现
所有读写redis命令在执行之前都会调用惰性删除的代码进行判断,惰性删除的代码如下:
1 | /* |
如果键已经过期,那么该函数会将输入的键从数据库之中删除,否则不做任何动作。当处理后的键仍然存在时,命令会按照存在的情况继续执行,否则按照不存在的情况执行。
定期删除实现
每当Redis的周期操作函数serverCron函数执行时,会调用activeExpireCycle函数进行过期键的清理。其会在规定的时间内,分次遍历数据库中的各个数据库,从expires字典中随机检查一部分过期时间,并删除其中的过期键。在扫描的时候,会提供一个全局变量记录上一次扫描的进度,并在下一次调用时,接着上一次的进度进行新一轮的检查工作。
AOF,RDB和复制功能处理过期键
AOF文件处理
当一个键已经过期时并被删除时,Redis会像AOF日志之中添加一条DEL命令,来记录该键已经被删除。在进行AOF重写的时候,程序会对数据库中的键进行检查,已过期的键不会保存到重写以后的AOF日志之中。
RDB文件处理
在生成RDB文件的时候,如果一个键已经过期,那么不会被保存到RDB文件之中。在载入RDB文件的时候,如果以主服务器的方式运行,那么过期的键不会被载入到数据库之中。如果以从服务器的方式运行,那么无论键是否过期都会被载入,因为在于主服务器同步的时候,从服务器的数据就会清空,所以对从服务器也没有影响。
复制功能处理
当处于服务器处于复制模式下时,服务器的过期删除键由主服务器控制。在主服务器删除一个过期键之后,会向从服务器发送一个DEL命令,告诉从服务器删除该键。从服务器即使键到达了过期时间也不会删除,知道收到DEL命令时才进行删除。通过主服务器统一的删除过期键可以保证主从一致性。