Redis-----第四部分 独立功能的实现

第四部分 独立功能的实现


1. 发布和订阅

​ Redis的发布与订阅功能由PUBLISH,SUBSCRIBE,PSUBSCRIBE等命令组成.

​ 通过执行SUBSCRIBE命令,客户端可以订阅一个或者多个频道,成为这些频道的订阅者.每当其他客户端向被订阅的频道发送消息时,这个频道的所有订阅者都能收到消息.

​ 通过PSUBSCRIBE命令,客户端可以订阅一个或者多个模式,一个模式可以与多个频道匹配,之间是多对多关系.客户端可以收到模式匹配的频道的消息.

(1). 频道的订阅和退订

​ 当一个客户端执行SUBSCRIBE命令订阅某个或者某些频道的时候,这个客户端与被订阅频道之间就建立起一种订阅关系.

​ Redis将所有频道的订阅关系存储在服务器状态的pubsub_channels字典里面.键是频道,值是一个链表,记录了所有与这个频道订阅的客户端.

struct redisServer{
    // .....
    
    // 保存所有频道的订阅关系
    dict *pubsub_channels;
    
    // .....
};
1). 订阅频道

​ 当客户端执行SUBSCRIBE命令进行订阅时,服务器会将客户端与被订阅的频道在pubsub_channel字典中关联.

  1. 如果频道已经有了订阅者,那么在字典中找到这个键,再其对应的链表中插入这个客户端节点
  2. 如果这个频道是初次被定义,那么在这个字典中创建一个键,并将这个键的值设置为空链表,然后在链表中插入当前节点.
2). 退订频道

​ UNSUBSCRIBE命令可以让客户端退订某个或者某些频道.

  1. 根据被退订频道的名字,在pubsub_channel字典中找到对应的频道信息,然后从链表中删除关于这个频道的信息
  2. 如果在删除客户端后,这个频道没有任何订阅者了,那么删除这个频道的键

(2). 模式的订阅与退订

​ 服务器将所有模式的订阅关系保存在服务器状态的pubsub_patterns:

struct redisServer{
    // .....
    
    // 保存所有模式的订阅关系
    list *pubsub_patterns;
    
    // .....
};

typedef struct pubsubPattern{
    // 订阅模式的客户端
    redisClient *client;
    
    // 被订阅的模式
    robj *pattern;
} pubsubPattern;

​ 这个属性和pubsub_channel结构不同,是一个链表,链表的每一个节点否包含一个pubsubPattern结构.

1). 订阅模式

​ 客户端执行PSUBSCRIBE命令订阅某个或某些模式时,服务器会对每个被订阅的模式执行以下操作:

  1. 新建一个pubsubPattern结构,将pattern设置为被订阅,client设置为当前执行的客户端
  2. 将pubsubPattern结构添加在pubsub_patterns链表的表尾.

在服务端中,每一个客户端对每一个模式的订阅都是一个节点.

2). 退订模式

​ 模式的退订命令PUNSUBSCRIBE是PSUBSCRIBE命令的反操作.会将链表中的符合客户端和模式的对应节点删除.

(3). 发送消息

​ 当一个Redis客户端执行PUBLISH <channel> <message>命令将消息message发送给频道channel执行时, 服务器执行以下动作:

  1. 将消息message发送给channel频道的所有订阅者
  2. 如果有一个或者多个模式pattern与频道channel相匹配,那么将message消息发送给pattern模式的订阅者.
1). 将消息发送给频道订阅者

​ PUBLISH命令首先要做的就是在服务器状态的pubsub_channels字典中找到频道channel的订阅者名单,也就是对应的那么链表.然后将消息发给所有的订阅者客户端.

2). 将消息发送给模式订阅者

​ PUBGLISH命令要做的就是遍历整个pubsub_pattern链表,查找那些与channel频道向匹配的模式,并发送订阅了这些模式的客户端.

频道相当于:news.it

模式相当于:news.*

(4). 查看订阅信息

​ PUBSUB命令是Redis 2.8新增的命令之一,可以通过这个命令来查看频道或者模式的相关信息.

1). PUBSUB CHANNELS

​ PUBSUB CHANNELS [pattern]子命令用于返回当前服务器当前被订阅的频道,pattern参数是一个匹配信息,如果加上,只会返回和pattern匹配的频道.

​ 这个自命令通过遍历服务器pubsub_channels字典的所有键来得到频道信息,有参数的话进行过滤.

2). PUBSUB NUMSUB

​ PUBSUB NUMSUB [channel-1 channel-2 … channel-n]子命令接受 任意多个频道作为输入参数,并返回这些频道的订阅者数量.

​ 这个命令是通过遍历pubsub_channels字典,返回其中所有匹配的频道对应的链表长度实现.

3). PUBSUB NUMPAT

​ PUBSUB NUMPAT子命令用于返回服务器当前被订阅模式的数量.

​ 该命令通过返回pubsub_patterns链表的长度来实现.

2. 事务

​ Redis通过MULTI,EXEC,WATCH等命令来实现事务功能.事务提供了将多个命令打包,然后一次性,按顺序地执行多个命令的机制.并且事务执行期间,不会被打断.

​ 一个事务以MULTI命令开始,然后将多个事务放入事务中,最后由EXEC命令将事务整体提交执行.

(1). 事务的实现

​ 事务的阶段:

  1. 事务开始
  2. 命令入队
  3. 事务执行
1). 事务开始

​ MULTI命令标记着事务的开始,它将客户端从非事务状态切换至事务状态,这一切换是通过对客户端的flags属性进行更改完成的.

2). 命令入队

​ 当客户端切换到事务状态后,服务器会根据这个客户端发来的不同命令执行不同的操作:

  • 如果发送的命令为EXEC,DISCARD,WATCH,MULTI洗个命令其中一个,那么立即执行这个命令
  • 如果不是,将这个命令让如一个事务队列里面,然后向客户端回复QUEUED.
3). 事务队列

​ 事务的状态存储在客户端状态中:

typedef struct redisClient{
    // .....
    // 事务状态
    multiState mstate;
    // .....
}redisClient;

// 事务状态
typedef struct multiState{
    // 事务队列,FIFO顺序
    // 是一个数组,先入队的命令在前,后入队在后
    multiCmd *commands;
    // 已入队命令数
    int count;
}multiState;

// 事务队列
typedef struct multiCmd{
    // 参数
    robj **argv;
    // 参数数量
    int argc;
    // 命令指针
    struct redisCommand *cmd;
}multiCmd;
4). 执行事务

​ 当一个处于事务状态的客户端向服务器发送EXEC命令时,这个命令立即被执行.服务器会遍历这个客户端的事务队列,执行队列中保存的命令,最后将执行的结果一次性返回给客户端.

(2). WATCH命令的实现

​ WATCH命令是一个乐观锁,可以在EXEC命令执行前,监视任意数量的数据库见,在EXEC命令执行时,检查这些件是否被修改,如果是,那么拒绝执行事务,并向客户端返回失败.

WATCH keyName
1). 使用WATCH命令监视数据库键

​ 每一个Redis数据库都保存着一个watched_keys字典,字典的键是某个被监视的数据库键,值是一个链表.其中记录了所有监视这个键的客户端.

typedef struct redisDb{
    // .....
    // 正在被WATCH命令监视的键
    dict *watched_keys;
    // .....
}redisDb;
2). 监视机制的触发

​ 所有对数据库进行修改的命令,在执行之后都会调用某函数对watched_keys字典进行检查,查看是否有客户嘟嘟呐正在监视更改修改过的键,如果有,那么打开这个客户端的REDIS_DIRTY_CAS表示,表示该客户端的事务安全性已经被破坏.

3). 判断事务是否安全

​ 当服务器接收到一个客户端发来的EXEC时:

  • 如果客户端的REDIS_DIRTY_CAS被打开,那么说明客户端监视的键中又被修改的了,服务器拒绝执行客户端提交的事务.
  • 如果没有,说明事务是安全的,服务器执行事务.

(3). 事务的ACID特性

1). 原子性

​ 对于Redis事务来讲,原子性是指事务中的命令要么全部执行,要么一个都不执行.

​ Redis事务不支持回滚(为了高效),即使事务中某个命令在执行时出现错误,事务也会继续执行下去.

2). 一致性

​ Redis通过谨慎的错误检测来保证事务的一致性(满足预定的约束).

1>. 入队错误

​ 例如:当客户端尝试向事务入队一个不存在的命令时,会被服务器拒绝执行.

2>. 执行错误
  • 执行错误都是不在入队时能被发现的错误
  • 即使发生了执行错误,也不会打断事务

​ 例如:命令操作的键的类型不符合,就会报错.

3>. 服务器停机

​ 根据服务器所使用的持久化方式,在服务器停机时,会有不同的情况:

  • 无持久化:重启后数据库是空白的,是一致的
  • RDB和AOF:可以根据现有的RDB文件或者AOF文件来恢复数据库到一个一致性状态
3). 隔离性

​ Redis是单线程串行的,并且事务不会被中断,所以不存在不满足隔离性的问题.

4). 持久性

​ Redis的持久性根据持久化的方式来决定.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值