《Redis设计与实现 黄建宏 著》第9章
该书基于Redis2.9,即Redis3.0开发版编写
1. 数据结构
Redis服务器数据结构
typedef struct
redisServer{
// ...
// 一个数组,保存服务器中的所有数据库
redisDb *db;
// 服务器的数据库数量,默认为16,可通过服务器配置中database选项修改
int dbnum;
// 记录RDB保存条件的数组
saveparam *saveparams;
// 修改计数器,上一次成功执行SAVE或BGSAVE后数据库状态的修改次数
long long dirty;
// 上一次成功执行SAVE或BGSAVE的时间,UNIX时间戳
time_t lastsave;
// ...
}redisServer;
Redis数据库数据结构
typedef struct
redisDb{
// ...
// 数据库键空间,保存数据库中的所有键值对
dict *dict;
// 过期字典,保存全部有过期时间数据库键的过期时间
dict *expires;
// ...
}redisDb;
注,因数据库键空间是一个字典,故所有对数据库的操作,实际上都是通过对字典进行操作来实现的
Redis客户端状态数据结构
typedef struct
redisClient{
// ...
// 记录客户端当前正在使用的数据库(指向redisServer.db数组其中一个元素)
redisDb *db;
// ...
}redisClient;
通过修改redisClient.db指针指向不同数据库,从而实现切换目标数据库的功能,这是
SELECT命令的实现原理
Redis命令对数据库进行读写时的额外维护操作(读、写操作都对键进行读取)
1) 读取一个键后,服务器会根据键是否存在去更新键空间命中或不命中次数,这两个值可在INFO stats命令的keyspace_hits和keyspace_misses属性中查看
2) 读取一个键后,服务器会更新键的LRU时间
3) 若服务器读取一个键时发现该键已过期,则会先删除该键,然后执行其他操作
4) 若有客户端使用
WATCH命令监视某个键,则服务器在对被监视的键进行修改后,会将该键标记为“脏”,从而让事务程序注意到该键被修改过
5) 服务器每修改一个键后,都会对“脏”键计数器的值+1,该计数器会触发服务器的持久化及复制操作
6) 若服务器开启了数据库通知功能,则在对键进行修改后,服务器将按配置发送相应的数据库通知
2. 过期键
4个设置键过期命令
·
EXPIRE 将键key的生存时间设置为ttl秒
·
PEXPIRE 将键key的生存时间设置为ttl毫秒
·
EXPIREAT 将键key的过期时间设置为timestamp指定的秒数时间戳
·
PEXPIREAT 将键key的过期时间设置为timestamp指定的毫秒数时间戳
注,
实际上EXPIRE、PEXPIRE、EXPIREAT3个命令都使用PEXPIREAT命令实现
PEXPIREAT命令为数据库键设置过期时间时,服务器会在过期字典中关联给定的数据库键和过期时间
过期字典
数据库中所有键的过期时间保存在过期字典(redisDb.expires)中
typedef struct
redisDb{
// ...
// 过期字典,保存键的过期时间
dict *expires;
// ...
}redisDb;
·过期字典的键是一个指针,该指针指向键空间中的某个数据库键
·过期字典的值是一个long long类型整数,该整数保存键所指向的数据库键的过期时间,一个毫秒精度UNIX时间戳
移除过期时间命令
PERSIST 解除数据库键和过期时间在过期字典中的关联
查看剩余生存时间命令
TTL 查看以秒为单位的剩余生存时间
PTTL 查看以毫秒为单位的剩余生存时间
过期键判定步骤
1) 检查给定键是否存在于过期字典,若存在,则取得键的过期时间,否则键未过期
2) 检查当前UNIX时间戳是否大于键的过期时间,若是,则键已过期,否则键未过期
过期删除策略
Redis服务器使用惰性删除和定期删除两种策略配合删除过期键
惰性删除策略
Redis服务器执行命令前会对输入键进行检查:
·若输入键已过期,则将该键从数据库删除
·若输入键未过期,则不做任何动作
定期删除策略
Redis服务器周期性执行以下过期键删除操作
·从一定数量数据库中取一定数量随机键进行检查,并删除其中的过期键
·记录检查进度,并在下次检查时接着上次的进度继续进行检查
·随着以上操作循环执行,服务器中所有数据库都会被检查一遍,然后再次开始新一轮检查工作
RDB对过期键的处理
生成RDB文件时,过期键不会被保存到新创建RDB文件中
载入RDB文件时,
若服务器以主服务器模式运行,过期键不会被载入到数据库
若服务器以从服务器模式运行,所有键,不论是否过期,都被载入到数据库
注,主从服务器进行数据同步时,从服务器数据库会被清空,故,从服务器载入过期键不会对数据一致性产生影响
AOF对过期键的处理
AOF写入文件时,
若数据库中某键已过期,但未被惰性删除或定期删除,则该过期键会继续被写入文件
当该过期键被惰性删除或定期删除后,程序向AOF文件追加DEL命令,显式地记录该键已被删除
AOF重写时,过期键不会被写入重写文件
复制模式对过期键的处理
从服务器过期键删除动作由主服务器控制:
·主服务器删除过期键后,显式地向所有从服务器发送DEL命令,告知从服务器删除该过期键
·从服务器执行客户端读命令时,即使碰到过期键也不会将过期键删除,而是继续像处理未过期键一样处理过期键
·从服务器只有在接到主服务器发来的DEL命令后,才会删除过期键
注,以上机制可保证主从服务器数据一致性
3. 数据库通知
客户端可通过订阅给定的频道或模式,获知数据库中键的变化,及数据库中命令的执行情况
2类通知
键空间通知(key-space notification),关注“某个键执行了什么命令”(以键为核心)
键事件通知(key-event notification),关注“某个命令被什么键执行了”(以命令为核心)
发送通知的配置
服务器配置中
notify-keyspace-events选项设置发送通知的类型,可选值及含义:
·AKE,发送所有类型的键空间通知和键事件通知
·AK,发送所有类型的键空间通知
·AE,发送所有类型的键事件通知
·K$,只发送和字符串键有关的键空间通知
·El,只发送和列表键有关的键事件通知
发送通知函数
void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid);
type,通知类型,程序根据该值判断该通知是否是服务器配置选定的通知类型,进而决定是否发送通知
event,事件名称
keys,产生事件的键
dbid,产生事件的数据库号码
函数根据上述4个参数构建事件通知的内容,及接收通知的频道名
发送通知函数的实现(过程)
1) 检查服务器是否允许发送该通知类型,若否,则直接返回
2) 检查服务器是否允许发送键空间通知,若是,则构建并发送事件通知
3) 检查服务器是否允许发送键事件通知,若是,则构建并发送事件通知