about redis - chris-wangkk/myWiki GitHub Wiki

1.expire key(过期键)
(1)Redis设置key过期时间的命令
(2)Redis是怎么实现过期时间设置,如何判断键过期
(3)过期键的删除策略以及Redis过期键的删除策略以及实现
(4)其他环节中是怎么处理过期键的呢(比如AOF、RDB)
-—>
(1)redis数据库在数据库服务器中使用了redisDb数据结构
typedef struct redisDb {
dict dict; / 键空间 key space,用来保存数据库中的所有键值对 /
dict *expires; /
过期字典 ,保存数据库中所有键的过期时间*/
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 /
struct evictionPoolEntry *eviction_pool; /
Eviction pool of keys /
int id; /
Database ID /
long long avg_ttl; /
Average TTL, just for stats */
} redisDb;
(2)设置过期时间命令
EXPIRE \ \:命令用于将键key的过期时间设置为ttl秒之后
PEXPIRE \ \:命令用于将键key的过期时间设置为ttl毫秒之后
EXPIREAT \ \:命令用于将key的过期时间设置为timrestamp所指定的秒数时间戳
PEXPIREAT \ \:命令用于将key的过期时间设置为timrestamp所指定的毫秒数时间戳

###过期时间保存以及判定
通过过期字段进行判定:
<1>检查给定键是否存在于过期字典,如果存在,取出键的过期时间
<2>通过判断当前UNIX时间戳是否大于键的过期时间,是的话,键已过期,相反则键未过期
(3)过期键删除策略
<1>定时删除:在设置键的过期时间的同时,创建一个定时任务,当键达到过期时间时,立即执行对键的删除操作
优点:定时删除策略可以保证过期键会尽可能快地被删除,并释放所占用的内存
缺点:对cpu时间不友好,在过期键比较多时,删除任务会占用很大一部分cpu时间,在内存不紧张但cpu时间紧张的情况下,将cpu时间用在删除和当前任务无关的过期键上,影响服务器的响应时间和吞吐量
-———>重空间轻时间

<2>惰性删除:放任键过期不管,但在每次从键空间获取键时,都检查取得的键是否过期,如果过期的话,就删除该键,如果没有过期,就返回该键
优点:对cpu时间友好,在每次从键空间获取键时进行过期键检查并是否删除,删除目标也仅限当前处理的键,这个策略不会在其他无关的删除任务上花费任何cpu时间
缺点:对内存不友好,过期键过期也可能不会被删除,导致所占的内存也不会释放。甚至可能会出现内存泄露的现象,当存在很多过期键,而这些过期键又没有被访问到,这会可能导致它们会一直保存在内存中,造成内存泄露
-———>轻空间重时间
实现:
在执行任何读写命令时都会先找到这个key,惰性删除就作为一个切入点放在查找key之前,如果key过期了就删除这个key
robj lookupKeyRead(redisDb *db, robj *key) {
robj *val;
expireIfNeeded(db,key); // 切入点.对输入键进行检查是否删除
val = lookupKey(db,key);
if (val == NULL)
server.stat_keyspace_misses++;
else
server.stat_keyspace_hits++;
return val;
}
int expireIfNeeded(redisDb *db, robj *key) {
/
取出键的过期时间 /
mstime_t when = getExpire(db,key);
mstime_t now;
/
没有过期时间返回0*/
if (when < 0) return 0; /* No expire for this key /
/
服务器loading时*/
if (server.loading) return 0;
/* 根据一定规则获取当前时间*/
now = server.lua_caller ? server.lua_time_start : mstime();
/* 如果当前的是从(Slave)服务器:0 认为key为无效;1 if we think the key is expired at this time. /
if (server.masterhost != NULL) return now > when;
/
key未过期,返回 0 /
if (now <= when) return 0;
/
删除键 */
server.stat_expiredkeys++;
propagateExpire(db,key,server.lazyfree_lazy_expire);

}

<3>定期删除:每隔一点时间,程序就对数据库进行一次检查,删除里面的过期键,至于要删除多少过期键,以及要检查多少个数据库,则由算法决定
-———><1>和<2>的折中方案
确定删除操作执行的时长和频率(太长会退化成定时删除策略),相反则退化成惰性删除。
实现:
key的定期删除会在Redis的周期性执行任务(erverCron,默认每100ms执行一次)中进行,而且是发生Redis的master节点,因为slave节点会通过主节点的DEL命令同步过来达到删除key的目的
。。。
依次遍历每个db(默认配置数是16),针对每个db,每次循环随机选择20个(ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)key判断是否过期,如果一轮所选的key少于25%过期,则终止迭次,此外在迭代过程中如果超过了一定的时间限制则终止过期删除这一过程

AOF、RDB和复制功能对过期键的处理
(1)RDB
生成RDB文件
程序会数据库中的键进行检查,已过期的键不会保存到新创建的RDB文件中
载入RDB文件
<1>主服务载入RDB文件,会对文件中保存的键进行检查会忽略过期键加载未过期键
<2>从服务器载入RDB文件,会加载文件所保存的所有键(过期和未过期的),但从主服务器同步数据同时会清空从服务器的数据库

(2)AOF
<1>AOF文件写入:当过期键被删除后,会在AOF文件增加一条DEL命令,来显式地记录该键已被删除
<2>AOF重写:已过期的键不会保存到重写的AOF文件中

(3)复制
当服务器运行在复制模式下时,从服务器的过期键删除动作由主服务器控制的,这样的好处主要为了保持主从服务器数据一致性
<1>主服务器在删除一个过期键之后,会显式地向所有的从服务器发送一个DEL命令,告知从服务器删除这个过期键
<2>从服务器在执行客户端发送的读取命令时,即使碰到过期键也不会将过期键删除,不作任何处理
<3>只有接收到主服务器 DEL命令后,从服务器进行删除处理

https://juejin.im/post/5da7144ff265da5ba532b753?utm_source=gold_browser_extension

⚠️ **GitHub.com Fallback** ⚠️