发布于订阅

频道订阅与退订

所有频道的订阅关系都保存在服务器的pubsub_channels字典

频道订阅1

  • 字典中的键是某个被订阅的频道
  • 字典中的值是订阅频道的客户端,是链表

频道订阅2

模式订阅与退订

所有模式订阅关系都存在服务器的pubsub_patterns链表中,链表中的节点结构pubsubPattern

模式订阅

示例:
模式订阅示例

发送消息

  • 发送给频道订阅者:在pubsub_channels字典中找到订阅者信息发送。
  • 发送给模式订阅者:遍历pubsub_patterns链表,找到与客户端相同模式的客户端,发送。

Redis 事务

事务的实现

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

1) 事务开始

redis> MULTI
OK

MULTI命令把客户端从非事务状态切换至事务状态——flags属性表示。

2) 命令入队

  • 当客户端处于非事务状态,命令会立即被服务器执行
  • 当客户端处于事务状态,根据命令判断执行
    • 如果客户端发送的命令为 EXECDISCARDWATCHMULTI 四个命令的其中一个,那么服务器立即执行这个命令。
    • 除此之外,不立即执行,放入事务队列,向客户端返回QUEUED回复.

事务流程图

整体结构:

  1. 事务状态:保存在服务器状态的mstate属性中
    typedef struct redisClient {
    
    // ...
    
    // 事务状态
    multiState mstate;      /* MULTI/EXEC state */
    
    // ...
    
    } redisClient;
    
  2. 事务状态
    typedef struct multiState {
    
    // 事务队列,FIFO 顺序
    multiCmd *commands;
    
    // 已入队命令计数
    int count;
    
    } multiState;
    
  3. 事务队列
    typedef struct multiCmd {
    
    // 参数
    robj **argv;
    
    // 参数数量
    int argc;
    
    // 命令指针
    struct redisCommand *cmd;
    
    } multiCmd;
    

示例:

  • 执行命令
    redis> MULTI
    OK
    
    redis> SET "name" "Practical Common Lisp"
    QUEUED
    
    redis> GET "name"
    QUEUED
    
    redis> SET "author" "Peter Seibel"
    QUEUED
    
    redis> GET "author"
    QUEUED
    
  • 结构图
    事务队列

3) 执行事务EXEC

服务器遍历事务队列,执行队列里的所有命令,最后把执行结果全部返回给客户端。

watch命令(乐观锁)

watch命令是一种乐观锁,可以在exec命令执行前,检测任意数量的数据库键,在exec命令执行时,检测键是否已经被修改,如果修改则拒绝执行,返回执行失败的空回复。 底层实现:

  1. Redis底层里有个watched_keys字典属性用于监测
    WATCH命令
  2. 如果被修改则打开REDIS_DIRTY_CAS标识,最后根据表示判断。 WATCH命令

事务的ACID性质

× 原子性

Redis不具备原子性,因为Redis不支持事务回滚,在事务队列里即使有命令出错,也会执行下去。

√ 一致性

如何保证一致性:

  • 入队错误:如果在入队命令过程中,命令不存在,格式错误,Redis拒绝执行。
  • 执行错误:事务执行过程中,命令出错,不会对数据库进行修改,不会对事务的一致性产生影响。
  • 服务器停机:
    • 无持久化模式下,重启依旧是空白,因此数据一致。
    • RDB模式下,事务中途停机不会导致不一致,因为可以恢复到一致状态。
    • AOF模式下,事务中途停机不会导致不一致,因为可以恢复到一致状态。

√ 隔离性

Redis采用单线程方式执行事务,并且服务器保证,运行期间事务不会中断,因此事务总是串行执行,具有隔离性。

√/× 持久性

Redis在RDB和AOF模式下才会持久化。