Redis(十二)
if(reach_time_limit()){
//达到上限就结束,等下一次的定期删除
return
}
}
}
重点
拥有全局变量来记录当前进度(current_db,记录检查过的数据库数量,同时记录第一个未检查过的数据库),还有配置文件上有默认的每次定期删除策略检查的数据库数量和检查每个数据库多少个键(即 DEFAULT_DB_NUMBER 和 DEFAULT_KEY_NUMBER)
函数每次运行时,都根据参数去数据库中扫描指定数量的数据库,每个数据库检查指定数量的键值对,并删除里面的过期键
如果在检查键值对的时候,函数执行时间达到了规定的时间,就会停止检查,这不会影响下一次的检查,因为 current_db 记录了下一个未检查的数据库,下一次会从这个数据库开始
当 current_db 等于服务器拥有的数据库数量,就代表所有数据库已经都被检查过了,需要重新开始新一轮的检查工作
[](()AOF、RDB 和复制功能对过期键的处理
RDB 和 AOF 是 Redis 的两种持久化策略,后面再详说,现在简单认识一下这两种持久化策略是怎么处理过期键的
[](()RDB
RDB 持久化策略是将数据持久化进磁盘的 RDB 文件里面。
[](()生成 RDB 文件
在执行 SAVE 命令或者 BGSAVE 命令会创建一个新的 RDB 文件,程序会对数据库中的键进行检查,如果键已经过期是不会被保存到新建的 RDB 文件中的。
因此,数据库中包含过期键不会对新生成的 RDB 文件造成影响(但旧的 RDB 文件可能会包含过期键,因为那时候键还没有过期,要等下一次覆盖 RDB 文件的时候才会刷新)。
[](()载入 RDB 文件
启动 Redis 服务器时,如果使用的是 RDB 功能的话,那么服务器将对 RDB 文件进行载入
载入情况又分为主服务器模式载入和从服务器模式载入
如果服务器以主服务器模式运行,那么在载入 RDB 文件时,程序会对 RDB 文件里面的键进行检查,如果过期了就不会载入,过期的了就会被忽略掉。
如果服务器以从服务器模式运行,那么在载入 RDB 文件时,程序会加载 RDB 里面所有的键,不论是否过期,虽然过期键被载入,不过在主从服务器进行同步更新的时候,从服务器的数据库会被清空,替换成主服务器的数据,过期键也在这里被清掉了。
所以,一般来说,过期键对载入 RDB 文件的从服务器也不会造成影响的
[](()AOF
AOF 持久化策略其实记录的是写入的 redis 命令,记录进 AOF 文件中,然后加载的时候,其实就是执行这些 redis 命令
[](()AOF 文件写入
当服务器以 AOF 持久化模式运行时,如果数据库中的某个键过期了,且这个键还没有被定期或者惰性删除,是不会 AOF 产生影响的。
当这个键被定期或者惰性删除时,AOF 文件也会追加一条 DEL 命令,去显示地标识这个键被删除
举个栗子
创建一个 5s 后过期的键,AOF 文件记录了该创建命令
5s 后使用访问该键,发现过期,会执行惰性删除策略(定期删除策略在这里也会执行删除),把键删除掉
同时在 AOF 文件中追加 DEL 命令
访问放回 nil 值
[](()AOF 文件重写
和生成 RDB 文件一样,已经过期的键不会被重写入 AOF 文件中,因此,数据库包含过期键不会对 AOF 造成影响
[](()复制功能
当服务器运行在复制模式下,从服务器的过期键删除动作由主服务控制
当主服务器在删除一个过期键后,会显示地向所有从服务器发送一个 DEL 命令,通知从服务器去删除该键值对《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】
从服务器在执行客户端发送的读命令时,及时读取的是过期键,也不会进行删除,而是像处理未过期的键一样去处理,返回过期键里面对应的值对象
从服务器只有在收到主服务器的 DEL 命令才会去删除键
[](()数据库通知
数据库通知可以让客户端通过订阅给定的频道或者模式,来获知数据库中键的变化,以及数据库中命令的执行情况
//客户端在指定频道上发布信息
publish channel message
//客户端订阅频道
subscribe channel
Redis 会自动发送上面所示的两种通知
第一种为键空间通知
第二种为键事件通知
foo 指的就是键值对对象的 key 指,所以 keyspce@0__foo 其实就是频道名称,del 是指对其执行的操作命令,或者 keyspace@0:del 是频道名称,foo 就是其操作的键值对对象 key 值
不过因为数据库通知是会占用 CPU 的,所以默认会关闭,要在配置文件进行开启
[](()键空间通知
键空间是数据库里面的属性,当用户在这个数据库里面进行对键值对的操作时候,Redis 就会发送通知给 keyspace@0 频道,所有关注了 keyspace@0 频道的用户都会接收这个消息
举个栗子
首先将配置文件的 notify-keyspace-events 的值改为 AK,标识开启键空间通知,并且接收所有类型
//关注了 0 号数据库的 message 对象
subscribe @keyspace@0:message
//开启另一个 redis,创建 message 为键名的字符串对象
set message haha
[](()键事件通知
与键空间通知不同,键空间通知是将对指定键的操作通知给订阅频道的客户端,而键事件通知是指将发送了指定事件的键值对对象的键名发送给订阅频道的客户端
举个栗子
首先将配置文件的 notify-keyspace-events 的值改为 AE,标识开启接收所有键事件通知。
//订阅 0 号数据库的 del 事件
subscribe keyevent@0:del
//另一个数据库执行 Del 事件
del msg
[](()notify-keyspace-events 的配置
这里简单介绍一下这个配置,详细可以看配置文件的注释
AKE:服务器发送所有类型的键空间通知和键事件通知
AK:服务器发送所有类型的键空间通知
AE:服务器发送所有类型的键事件通知
K$:只发送和字符串有关的键空间通知
EI:只发送和列表键有关的键空间通知
[](()发送通知的实现原理
发送数据库通知的功能是由 notify.c/notifyKeyspaceEvent 函数去执行的
**void notifyKeyspaceEvent(int type,char event,robj key,int dbid);
type 其实就是当前想要发送的通知的类型,程序会根据这个值去判断当前发送通知的类型是否跟配置文件里面的 notify-keyspace-event 匹配,从而决定通知是否发送
event 是事件的名称,用字符指针来实现字符串,比如删除事件为 DEL,过期事件为 EXPIRE
key 是产生事件的键对象
dbid 是数据库号
当开启了数据库通知之后,当执行 Redis 命令之后,命令的实现函数在实现操作成功后都会调用 notify-KeyspaceEvent 函数,去判断是否需要发送通知
实现的伪代码
def notifyKeyspaceEvent(type,event,key,dbid);
//先判断是否符合配置文件的设定的要求
//如果不符合直接结束
if not(server.notify_keyspace_events & type) return;
//符合条件进行发送通知
//如果配置的要求是键空间,则发送键空间通知
if(server.notify_keyspace_events & REDIS_NOTIFY_KEYSPACE)
//构建频道名字
channel = "keyspace@{dbid}:{key}".format(dbid=dbid,key=key)
评论