Redis服务器数据库的实现
redis服务器将所有数据库都保存在redis.h/redisServer结构体中的Db数组中,每个Db数组都是一个redis.Db结构。初始化数据库时会根据redisServer结构中的dbnum属性来决定数据库的多少。
struct redisServer{
...
redisDb *db;//一个数组,保存者服务器中的所有数据库
int dbnum;//服务器的数据库数量根据配置文件生成
...
}
typedef struct redisDb{
...
dict *dict;//数据库的键空间保存数据库的所有键值对
dict *expires;//过期字典
...
}
对数据库的键值操作实际就是对一个字典的键值操作。Redis的所有key-value值都存在于这个大的字典中,默认16个数据库就存在16个字典
如果需要切换数据库,redisClient结构的Db属性,一个指向redisDb的指针,切换实际就是对这个指针进行操作。
键的生存时间或过期时间原理
EXPIRE或PEXPIRE 命令是将生存时间设置为多少秒和毫秒
EXPIREAT或PEXPIREAT命令是将过期时间设置为所输入的时间戳 这四个命令最终都转换为PEXPIREAT,它们的过期时间都是一个UNIX时间戳。
保存过期时间的是一个存在于上文redisDB结构中的字典,这个字典被称为过期字典,value为longlong类型,用来保存key所对应的过期UNIX时间戳。
PERSIST移除过期时间实际是删除过期字典中的这个键
怎么判断键是否过期?
1.先检查这个键是否存在于过期字典:如果存在,取得过期时间时间戳
2.检查当前时间戳与过期时间戳得大小判断是否过期。
TTL或者PTTL如果返回大于等于0就说明未过期。
Redis过期键的删除策略
惰性删除+定期删除两种
惰性删除:只有在取出键进行使用时才会对键进行过期检查并且判断删除
定期删除:每相隔一定时间进行判断删除
Redis为什么不单一使用一种策略进行删除?
如果使用惰性删除:如果存在很多过期键但没有被访问,会非常的消耗内存。
如果使用定期删除:难以直接确定定期删除的时长和频率,如果太过于频繁就会退化为定时删除,以至于将cpu时间过多的消耗在删除过期键上面。如果执行频率过少就会退化为惰性删除,出现浪费内存的情况。
所以Redis使用惰性删除+定期删除两种策略。
实现:
惰性删除策略:
所有读写数据库的Redis命令在执行前都会调用expireIfNeeded函数对输入键进行检查进行判断是否过期,如果过期删除键之后继续执行相对应得命令,如果未过期则直接执行相应输入得命令。
定期删除策略:
定期删除的函数activeExpireCycle存储在serverCron函数中,这个函数会在过期字典中随机抽取一定量的键进行检查判断,并且记录位置。
eg:第一次随即查询0号数据库,第二次就会随机1号。
RDB、AOF对过期键的处理
SAVE和BGSAVE会对过期键进行检查,已过期的键不会被保存到RDB文件中,载入RDB文件时主服务器会对过期字典及进行检查,从服务器不论是否过期全部载入(主从模式:数据库状态同步时会将从服务器数据库清空)
AOF中键过期或会输入DEL命令。在执行AOF重写的过程中也会对数据库中的键进行检查,已经过期的键不会被写入