1.概述
Redis服务器将所有的数据库都保存在redis.h/redisServer结构的db数组中,db数组每个项都是一个redis.h/redisDb结构,每个redisDb代表一个数据库。
默认情况下dbNum=16,所以Redis服务器默认会创建16个数据库。
默认情况下,redis客户端的目标数据库为0号数据库,客户端可以通过执行SELECT命令来切换数据库。
2.实现
源码:
struct redisServer {
/* General */
// 配置文件的绝对路径
char *configfile; /* Absolute config file path, or NULL */
// serverCron() 每秒调用的次数
int hz; /* serverCron() calls frequency in hertz */
// 数据库
redisDb *db;
// 数据库个数
int dbnum;
...........
}
/* Redis database representation. There are multiple databases identified
* by integers from 0 (the default database) up to the max configured
* database. The database number is the 'id' field in the structure. */
typedef struct redisDb {
// 数据库键空间,保存着数据库中的所有键值对
dict *dict; /* The keyspace for this DB */
// 键的过期时间,字典的键为键,字典的值为过期事件 UNIX 时间戳
dict *expires; /* Timeout of keys with a timeout set */
// 正处于阻塞状态的键
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP) */
// 可以解除阻塞的键
dict *ready_keys; /* Blocked keys that received a PUSH */
// 正在被 WATCH 命令监视的键
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
struct evictionPoolEntry *eviction_pool; /* Eviction pool of keys */
// 数据库号码
int id; /* Database ID */
// 数据库的键的平均 TTL ,统计信息
long long avg_ttl; /* Average TTL, just for stats */
} redisDb;
例子:
3.生存/过期时间
3.1 设置过期时间
redis有四个不同的命令可以用于设置键的生存时间或者过期时间,四种命令最终执行效果都是使用pexpire实现的。
expire <key> <ttl> 将key的生存时间设置为ttl秒
pexpire <key> <ttl> 将key的生存时间设置为ttl毫秒
expireat <key> <timestamp> 将key的过期时间设置为timestamp所指定的秒数时间戳
pexpireat <key> <timestamp> 将key的过期时间设置为timestamp所指定的毫秒数时间戳
过期判断逻辑:
1.获取键的过期时间
2.键没有设置过期时间直接返回false
3.获取当前时间的unix时间戳
4.检查当前时间是否大于键的过去时间
4.1 如果是,返回true
4.2 如果否,返回false
3.2 过期key删除策略
先说说过期的策略有哪些:
1.定时删除:在创建key的同时,创建一个定时器,让定时器在过期时间的时候将key删除。
优点:尽快的删除过期的key,并释放内存
缺点:在过期key比较多的时候,过多的占用cpu资源
2.惰性删除:放任key过期不管,每次获取key的时候判断过期时间,过期了则删除。
优点:不会额外占用cpu资源
缺点:过期的key没有被删除,占用内存
3.定期删除:每个一段时间对数据库进行一次检查,删除过期的key。
相比1和2的方案,定期删除是一种折中方案。
定期时间间隔太短:占用cpu资源
定期时间间隔太长:过期的key没有被删除,占用内存
redis使用的策略是:2.惰性删除、3.定期删除
3.2.1 惰性删除策略的实现
所有读写数据库的redis命令执行之前都会调用db.c/expireIfNeeded函数进行检查。
3.2.3 定期删除策略的实现
过期key的定期删除策略由redis.c/activeExpireCycle实现,通过redis.c/serverCron定时触发。
1.activeExpireCycle每次执行都从一定数据库中取出一定数量的随机key进行检查,并删除过期的key。
2.全局变量current_db会记录activeExpireCycle的进度,下次触发就按照上一次的进度处理。
3.当所有的数据库都被执行一遍之后,current_db=0。然后开始下一轮检查。
4.AOF、RDB和复制功能对过期key的处理
4.1 RDB
在执行SAVE/BGSAVE生成RDB文件时,已经过期的key不会被保存到新创建的RDB文件里面。
在载入RDB文件的时候,如果是master服务器,会将过期的key过滤掉,slave服务器不管过不过期都会加载到数据库,等master服务器数据同步的时候在将过期的key删除。
4.1 AOF
如果key过期,但是没有被惰性和定期删除,不影响AOF文件写入,当key被惰性和定期删除时,程序会向AOF文件append一条DEL语句。
在执行AOF重写的时候,过期的key不会被写入AOF文件中。
4.2 主从复制
从服务器的过期key删除操作由主服务器控制,主服务器删除一个key的时候,会向所有服务器发送一个DEL命令;从服务在接口客户端读命令的时候,即使key过期也也不会做处理,只有接受到主服务器的DEL命令,才会删除过期key。