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

第四部分 独立功能的实现


[toc]


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的持久性根据持久化的方式来决定.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266