zoukankan      html  css  js  c++  java
  • 《Redis 设计与实现》读书笔记(四)

    独立功能的实现

    十八、发布和订阅

    发布和订阅由下面几条命令组成

    • PUBLISH,发布消息,例如PUBLISH <频道名> <消息内容>
    • SUBSCRIBE,订阅某个频道 SUBSCRIBE <频道名>
    • UNSUBSCRIBE 退订某个频道
    • PSUBSCRIBE,订阅某个模式频道,例如news.* PSUBSCRIBE <模式频道名>
    • PUNSUBSCRIBE,退订某个模式频道

    数据结构

    • redisServer
      • dict *pubsub_channels key是频道名,value是一个链表,保存所有订阅这个频道的客户端
      • list *pubsub_patterns 链表,值是pubsub_pattern对象,保存客户端和模式频道

    操作

    • 订阅频道。例如命令 SUBSCRIBE news.it
      • 查看pubsub_channels 字典是否有news.it这个key,如果有,把客户端添加到链表后面,如果没有,新建一个链表,设置news.it这个key指向这个链表
    • 退订频道。例如命令 UNSUBSCRIBE news.it
      • 找到news.it对应的链表,遍历链表,找出当前客户端,然后删除
    • 订阅模式频道。例如命令 PSUBSCRIBE news.*
      • 在pubsub_patterns 链表后面添加一个pubsub_pattern对象,保存这个客户端的信息和news.*
    • 退订模式频道。例如命令 PUNSUBSCRIBE news.*
      • 遍历pubsub_patterns 链表,删除客户端等于当前客户端,模式等于news.*的对象
    • 发布消息,例如 PUBLISH news.it news11
      • 遍历pubsub_channels[news11]链表,把消息发送给链表中的所有客户端
      • 遍历pubsub_patterns链表,如果模式和news11匹配,就发送消息给对应的客户端

    其他命令

    这些命令是2.8后新增的

    • PUBSUB CHANNELS [pattern] 模式是可选的
      • 如果没有pattern 返回服务端所有被订阅的频道
      • 如果有,返回匹配上pattern的所有频道
    • PUBSUB NUMSUB [channel-1 channel-2 。。。] 可以输入多个频道
      • 返回每个频道的订阅者数量,也就是pubsub_channels[channel-1]链表的长度
    • PUBSUB NUMPAT
      • 返回服务端当前被订阅的模式频道数量,也就是pubsub_patterns 链表的长度。

    十九、事务

    Redis通过MULTI EXEC WATCH命令来实现事务

    事务例子

    MULTI
    get test
    set test 111
    EXEC
    

    数据结构

    • redisClient 客户端对象
      • multiState mstate 事务状态
    • multiState 事务状态对象
      • multiCMD *commands 保存改事务下面的所有命令FIFO原则
      • int count 已入队的命令数量
    • multiCmd 命令对象
      • robj **argv 命令参数
      • int argc 参数数量
      • struct redisCommand * cmd 命令对象

    流程

    • 当执行MULTI命令,改客户端进入事务模式。会打开客户端对象的flags属性的REDIS_MULTI标识
    • 当执行get和set命令,会把这些命令的信息加入到commands队列。但是不会立刻执行
    • 当执行EXEC命令。服务端把客户端的commands命令拿出来,逐条执行,然后返回每条的结果
    • 在MULTI和EXEC命令之间的所有命令,都不会立刻执行。
    • 执行完EXEC命令后,清空客户端对象的事务状态,包括flags标识,multiState对象等

    WATCH命令

    WATCH命令是一个乐观锁,可以在EXEC命令开始前监视任意数量的数据库键。
    例如事务
    WATCH name
    MULTI
    set name kevinlu
    EXEC

    如果在WATCH后,EXEC前,name这个键被修改了,执行EXEC后,会返回空给客户端。

    实现:

    • redisDb的对象的dict *watched_keys变量用于保存当前watch命令的所有key和客户端对象。key是数据库的键,value是链表,值是客户端对象
    • 当执行WATCH命令,Redis会在watched_keys加上数据
    • 当Redis执行修改命令时,会执行touchWatchKey操作。具体就是在watched_keys找出修改的key的链表,对链表中的所有客户端对象,打开客户端的REDIS_DIRTY_CAS标识,表示这些客户端WATCH的key中至少有一个被修改了
    • 当客户端执行EXEC命令时,检查REDIS_DIRTY_CAS标识是否被打开,如果是,返回空。否则才执行事务。

    事务性质(ACID)

    • A 原子性。因为Redis把事务打包在一起执行,所以是具有原子性的。要不成功,要不失败。但是Redis不会有事务回滚,也就是例如第2条命令失败了,第1条命令不会回滚,第3条命令也会继续执行。作者是命令回滚太复杂,不适合Redis的简单原则。
    • C 一致性。事务执行前和执行后都能保证一致性。一致性是指不会破坏数据库的设计(这个好笼统,不知道具体的边界)。Redis的命令执行错误主要有两种:
      • 入队错误。例如命令不存在。这个在EXEC前就会被发现并被Redis拒绝。所以这个不会破坏一致性。
      • 执行错误。命令执行的时候错误了,例如key是string类型,却用rpush操作。对于这种错误,Redis不会终止事务的执行,也不会回滚。所以这些命令不会执行,所以也不会破坏一致性(不太明白这个,都有一个条命令执行失败了,怎么会不破坏一致性?)
      • 数据库停机。如果不保存数据,那一致性就不用考虑了,因为重启后都是空白数据库。如果保存数据,事务会落地,所以也能保证一致性
    • I 隔离性。由于Redis是单线程执行,所以不会有事务并发问题。所以可以保证隔离性。
    • D 耐久性。事务执行成功后保证可以持久化到数据库。由于Redis在执行指定命令,例如BGSAVE的时候,才会进行持久化,所以不能保证耐久性。
      • 可以使用AOF 设置appendfsync来保证耐久性
  • 相关阅读:
    MongoDB4.0以下版本,同一台电脑安装2个MongoDB服务
    CMake编译Mysql connector C++
    Winsock I/O方法
    查看mysql版本的四种方法(转)
    WorkBench,DELETE 标准语句失败
    Qt 透明对话框 自定义透明度
    QString 分割字符串时产生乱码的问题
    winsock error 相关
    线程的分离状态与结合状态
    Oracle 语法
  • 原文地址:https://www.cnblogs.com/Xjng/p/12085126.html
Copyright © 2011-2022 走看看