Redis-9.8 数据库通知

9.8 数据库通知
数据库通知是Redis 2.8版本新增加的功能,这个功能可以让客户端通过订阅给定的频道或者模式,来获知数据库中键的变化,以及数据库中命令的执行情况。
举个例子,以下代码展示了客户端如何获取0号数据库中针对message键执行的所有命令:
127.0.0.1:6379> SUBSCRIBE _ _keyspace@0_ _:message
Reading messages... (press Ctrl-C to quit)
1) "subscribe"  //订阅信息
2) "__keyspace@0__:message"
3) (integer) 1
1) "message"    //执行SET命令
2) "_ _keyspace@0_ _:message"
3) "set"
1) "message"    //执行EXPIRE命令
2) "_ _keyspace@0_ _:message"
3) "expire"
1) "message"    //执行DEL命令
2) "_ _keyspace@0_ _:message"
3) "del"
根据发回的通知显示,先后共有SET、EXPIRE、DEL三个命令对键message进行了操作。
这一类关注“某个键执行了什么命令”的通知称为键空间通知(key-space notification),除此之外,还有另一类称为键事件通知(key-event notification)的通知,它们关注的是“某个命令被什么键执行了”。
以下是一个键事件通知的例子,代码展示了客户端如何获取0号数据库中所有执行DEL命令的键:
127.0.0.1:6379> SUBSCRIBE _ _keyevent@0_ _:del
Reading messages... (press Ctrl-C to quit)
1) "subscribe"  //订阅信息
2) "_ _keyevent@0_ _:del"
3) (integer) 1
1) "message"    //键key执行了DEL命令
2) "_ _keyevent@0_ _:del"
3) "key"
1) "message"    //键number执行了DEL命令
2) "_ _keyevent@0_ _:del"
3) "number"
1) "message"    //键message执行了DEL命令
2) "_ _keyevent@0_ _:del"
3) "message"
根据发回的通知显示,key、number、message三个键先后执行了DEL命令。
服务器配置的notify-keyspace-events选项决定了服务器所发送通知的类型:
❑想让服务器发送所有类型的键空间通知和键事件通知,可以将选项的值设置为AKE。
❑想让服务器发送所有类型的键空间通知,可以将选项的值设置为AK。
❑想让服务器发送所有类型的键事件通知,可以将选项的值设置为AE。
❑想让服务器只发送和字符串键有关的键空间通知,可以将选项的值设置为K$。
❑想让服务器只发送和列表键有关的键事件通知,可以将选项的值设置为El。
关于数据库通知功能的详细用法,以及notify-keyspace-events选项的更多设置,Redis的官方文档已经做了很详细的介绍,这里不再赘述。
在接下来的内容中,我们来看看数据库通知功能的实现原理。
9.8.1 发送通知
发送数据库通知的功能是由notify.c/notifyKeyspaceEvent函数实现的:
void notifyKeyspaceEvent(int type,char *event,robj *key,int dbid);
函数的type参数是当前想要发送的通知的类型,程序会根据这个值来判断通知是否就是服务器配置notify-keyspace-events选项所选定的通知类型,从而决定是否发送通知。
event、keys和dbid分别是事件的名称、产生事件的键,以及产生事件的数据库号码,函数会根据type参数以及这三个参数来构建事件通知的内容,以及接收通知的频道名。
每当一个Redis命令需要发送数据库通知的时候,该命令的实现函数就会调用notify-KeyspaceEvent函数,并向函数传递传递该命令所引发的事件的相关信息。
例如,以下是SADD命令的实现函数saddCommand的其中一部分代码:
void saddCommand(redisClient*c){
     // ...
     //
     如果至少有一个元素被成功添加,那么执行以下程序
     if (added) {
          // ...
          //发送事件通知
          notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[1],c->db->id);
     }
     // ...
}
当SADD命令至少成功地向集合添加了一个集合元素之后,命令就会发送通知,该通知的类型为REDIS_NOTIFY_SET(表示这是一个集合键通知),名称为sadd(表示这是执行SADD命令所产生的通知)。
以下是另一个例子,展示了DEL命令的实现函数delCommand的其中一部分代码:
voi delCommand(redisClient *c){
    int deleted=0,j;
    //遍历所有输入键
    for (j=1; j<c->argc; j++){
        //尝试删除键
        if (dbDelete(c->db,c->argv[j])){
            // ...
            //删除键成功,发送通知
            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,
                "del",c->argv[j],c->db->id);
            // ...
        }
    }
    // ...

}

在delCommand函数中,函数遍历所有输入键,并在删除键成功时,发送通知,通知的类型为REDIS_NOTIFY_GENERIC(表示这是一个通用类型的通知),名称为del(表示这是执行DEL命令所产生的通知)。
其他发送通知的函数调用notifyKeyspaceEvent函数的方式也和saddCommand、delCommand类似,只是给定的参数不同,接下来我们来看看notifyKeyspaceEvent函数的实现。
9.8.2 发送通知的实现
以下是notifyKeyspaceEvent函数的伪代码实现:
def notifyKeyspaceEvent(type, event, key, dbid):
    #如果给定的通知不是服务器允许发送的通知,那么直接返回
    if not(server.notify_keyspace_events & type):
        return
    #发送键空间通知
    if server.notify_keyspace_events & REDIS_NOTIFY_KEYSPACE:
        #将通知发送给频道__keyspace@<dbid>__:<key>
        #内容为键所发生的事件 <event>
        #构建频道名字
        chan = "__keyspace@{dbid}__:{key}".format(dbid=dbid, key=key)
        #发送通知
        pubsubPublishMessage(chan, event)
    #发送键事件通知
    if server.notify_keyspace_events & REDIS_NOTIFY_KEYEVENT:
        #将通知发送给频道__keyevent@<dbid>__:<event>
        #内容为发生事件的键 <key>
        #构建频道名字
        chan = "__keyevent@{dbid}__:{event}".format(dbid=dbid,event=event)
        #发送通知
        pubsubPublishMessage(chan, key)
notifyKeyspaceEvent函数执行以下操作:
1)server.notify_keyspace_events属性就是服务器配置notify-keyspace-events选项所设置的值,如果给定的通知类型type不是服务器允许发送的通知类型,那么函数会直接返回,不做任何动作。
2)如果给定的通知是服务器允许发送的通知,那么下一步函数会检测服务器是否允许发送键空间通知,如果允许的话,程序就会构建并发送事件通知。

3)最后,函数检测服务器是否允许发送键事件通知,如果允许的话,程序就会构建并发送事件通知。
另外,pubsubPublishMessage函数是PUBLISH命令的实现函数,执行这个函数等同于执行PUBLISH命令,订阅数据库通知的客户端收到的信息就是由这个函数发出的,pubsubPublishMessage函数具体的实现细节可以参考第18章。


9.9 重点回顾
❑Redis服务器的所有数据库都保存在redisServer.db数组中,而数据库的数量则由redisServer.dbnum属性保存。
❑客户端通过修改目标数据库指针,让它指向redisServer.db数组中的不同元素来切换不同的数据库。
❑数据库主要由dict和expires两个字典构成,其中dict字典负责保存键值对,而expires字典则负责保存键的过期时间。
❑因为数据库由字典构成,所以对数据库的操作都是建立在字典操作之上的。
❑数据库的键总是一个字符串对象,而值则可以是任意一种Redis对象类型,包括字符串对象、哈希表对象、集合对象、列表对象和有序集合对象,分别对应字符串键、哈希表键、集合键、列表键和有序集合键。
❑expires字典的键指向数据库中的某个键,而值则记录了数据库键的过期时间,过期时间是一个以毫秒为单位的UNIX时间戳。
❑Redis使用惰性删除和定期删除两种策略来删除过期的键:惰性删除策略只在碰到过期键时才进行删除操作,定期删除策略则每隔一段时间主动查找并删除过期键。
❑执行SAVE命令或者BGSAVE命令所产生的新RDB文件不会包含已经过期的键。
❑执行BGREWRITEAOF命令所产生的重写AOF文件不会包含已经过期的键。
❑当一个过期键被删除之后,服务器会追加一条DEL命令到现有AOF文件的末尾,显式地删除过期键。
❑当主服务器删除一个过期键之后,它会向所有从服务器发送一条DEL命令,显式地删除过期键。
❑从服务器即使发现过期键也不会自作主张地删除它,而是等待主节点发来DEL命令,这种统一、中心化的过期键删除策略可以保证主从服务器数据的一致性。
❑当Redis命令对数据库进行修改之后,服务器会根据配置向客户端发送数据库通知。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值