zoukankan      html  css  js  c++  java
  • Redis学习笔记(三):发布与订阅、事务

    独立功能的实现
     
    1.发布与订阅
     
    Redis的发布与订阅功能由 publish,subscribe,psubscribe等命令组成
     
    客户端订阅一个或多个频道
    > subcribe "news.it" "news.et"   //订阅两个频道
     
    客户端向频道发送消息
    > publish "news.it" "hello"      //向news.it频道发送消息hello
    订阅了news.it频道的客户端都会收到hello消息
     
    客户端订阅一个或多个模式
    > psubscribe "news.*" "book.*"   //相当于订阅了匹配这两个模式中任意一个的所有频道
     
    客户端退订频道
    > unsubscribe "news.sport"
    客户端退订模式
    > punsubscribe "book.*"
     
    2.频道订阅原理
    当客户端执行subscribe命令订阅频道时,客户端与频道就建立了一种订阅关系。该关系存储在服务器状态的 pubsub_channels 字典里。
    字典键为订阅的频道;字典值为一个链表,链表中记录了所有订阅该频道的客户端
     
    订阅频道时,根据是否是该频道的第一个订阅者进行不同处理
        1、是第一个订阅者,创建一个频道对应的键(是字符串对象),并将键值设置为空链表结构,然后将客户端添加到链表中。
        2、不是第一个订阅者,则已存在键,那么直接添加到对应的值链表结构的末尾。
     
    退订频道时,根据是否是最后一个订阅者进行不同处理
        1、是最后一个订阅者,则删除退订客户端后,继续删除频道在字典中对应的键
        2、不是最后一个订阅者,则直接根据键找到对应的值链表结构,然后遍历删除退订客户端即可
     
    模式订阅原理
    模式的订阅关系保存在服务器状态的 pubsub_patterns 链表中
    pubsub_patterns链表的每个节点都包含一个pubsubPattern结构
    pubsubPattern结构包含pattern属性和client属性(这两个属性都是简单字符串构成,或者说这两个一起就是一个字符串对象?)
     
    订阅模式
        新建一个pubsubPattern结构,将其pattern属性设置为对应模式,client属性设置为订阅者,然后添加到当前pubsub_patterns链表的表尾。
     
    退订模式
        查找pattern属性为退订模式,client属性为退订者的pubsubPattern结构即可。
     
    发送消息流程与原理
    > publish <channel> <message>   //channel为频道

    1、将消息发送给所有订阅该频道的订阅者
            原理:在pubsub_channels字典中找到对应的键,然后按照对应的值链表结构中的客户端依次发送消息
     
    2、将消息发送给予该频道相匹配的模式的订阅者
            原理:在pubsub_patterns链表中遍历,找到所有匹配该频道的模式(pattern属性),并将消息发送给他们对应的客户端(client属性)
     
     
    查看订阅信息的pubsub的三个子命令
    > pubsub channels [pattern]     //查询服务器当前被订阅的所有频道,[]内为可选参数,可用于匹配限制
     
    > pubsub numsub [channel1 channel2 ...]   //返回这些频道的订阅者数量,即订阅者键对应的值链表结构的长度
     
    > pubsub numpat                //返回服务器当前被订阅的模式的数量,即返回pubsub_patterns链表的长度
     
    3.事务(transaction)
     
    概念:将一种或多个命令打包,然后一次性、按顺序的统一执行。
     
    相关命令
     
    监听事务中的数据库键
    > watch <key_name>    //key_name为要监听的数据库键,用于判断数据库键是否在事务过程中被其他客户端修改
     
    开启事务
    > multi
     
    提交并执行事务
    > exec
     
    注意:事务中的多个命令被一次性发送给服务器,而不是一条一条发送,这种方式被称为pipeline。 pipeline 可以一次性发送多条命令并在执行完后一次性将结果返回,可以减少客户端与服务器之间的网络通信次数从而提升性能,并且 pineline 基于队列,而队列的特点是先进先出,这样就保证数据的顺序性。
     
    事务的实现
    一般分为三个阶段
     
    1.事务开始
        使用multi命令,执行该命令的客户端从非事务状态切换到事务状态
        客户端状态的flags属性会打开 REDIS_MULTI标识,以标志该客户端开启事务,处于事务状态
     
    2.命令入队
        非事务状态下,Redis客户端发送的命令会被服务器直接执行
        事务状态下,Redis服务器会将客户端发送的命令放入一个事务队列,并返回QUEUE(exec,discard,watch,multi四个命令除外)
     
        每个客户端有自己的事务状态,该状态保存在客户端的mstate属性里(是一个multiState结构,内含一个FIFO事务队列和一个计数器count)
     
        事务队列是一个multiCmd数组,每个multiCmd元素都存储了一个入队命令的信息(函数指针,命令参数,参数数量)
     
        count计数器则用来统计事务队列的长度。
     
    3.事务执行
        客户端发送 exec 命令后
        服务器遍历客户端的事务队列,单线程串行执行事务队列中的事务,并将每个命令的结果依次返回给客户端
     
     
    Watch命令
     
        watch命令是一个乐观锁,在exec命令前执行,用来监视数据库键在事务建立过程中是否发生改变
        watch命令字exec命令执行时,会检查其监视的键是否发生了修改;如果发生修改,则服务器会拒绝执行事务。
     
        底层实现
            每个Redis服务器都保存了一个 watched_keys 字典,
            该字典的键为被监视的数据库键,值是一个链表结构,链表中记录了所有监视该数据库键的客户端
        
        原理实现
            一旦服务器执行了对数据库进行修改的命令(如SET,LPUSH,DEL,ZADD等),便会在执行后调用一个函数对 watched_keys字典进行检查,查看被监视的键是否发生了修改;有,则将监视该数据库键的所有客户端的REDIS_DIRTY_CAS标识打开,以表示客户端事务安全性被破坏。而执行exec命令时,会对客户端的REDIS_DIRTY_CAS标识进行判断,如果发现该标识已打开,则认为事务不安全,并拒绝执行该事务。
     
    Redis事务的ACID
        原子性、一致性、隔离性、持久性
     
        Redis事务总具有原子性、一致性、隔离性;特定情况下也会具有持久性(AOF持久化模式下,且appendfsync设置为always)
     
        一致的概念:数据符合数据库的定义和要求,不包含非法或无效的错误数据
        
        Redis从三个方面维护一致性(错误检测和设计来维护)
            1.入队错误
                若命令不存在、格式不正确等,入队过程会报错,而服务器拒绝执行入队过程中出现错误的事务
     
            2.执行错误
                如命令正确但是操作键对象不符合等(SET操作列表等),出错命令被服务器识别,并进行相应错误处理,但错误命令不会对数据库进行任何修改
     
            3.服务器停机
                (1)RDB、AOF持久化维护
                (2)如果找不到对应文件,或处于无持久化模式下,则重启后数据库为空白,仍符合一致的要求
     
        Redis通过单线程、事务队列串行执行某个事务的命令,所以即使多个客户端事务并发,仍然不会互相影响
     
        要了解为什么大部分情况无法维护持久性(如RDB持久化,如appendfsync=everysec/no时的持久化为什么不能维护)
        
    Redis事务和传统关系型数据库事务最大区别
        Redis不支持事务的回滚(roll back),执行事务命令期间,即使发现命令执行错误,整个事务也会继续执行下去,直到将该客户端的对应的事务队列中所有命令执行完毕。(问题是这样不就不满足原子性了吗?Redis作者认为这种情况是存在编译错误,实际生产中不会出现?)
  • 相关阅读:
    pat 甲级 1065. A+B and C (64bit) (20)
    pat 甲级 1064. Complete Binary Search Tree (30)
    pat 甲级 1010. Radix (25)
    pat 甲级 1009. Product of Polynomials (25)
    pat 甲级 1056. Mice and Rice (25)
    pat 甲级 1078. Hashing (25)
    pat 甲级 1080. Graduate Admission (30)
    pat 甲级 团体天梯 L3-004. 肿瘤诊断
    pat 甲级 1099. Build A Binary Search Tree (30)
    Codeforce 672B. Different is Good
  • 原文地址:https://www.cnblogs.com/xiang9286/p/11003922.html
Copyright © 2011-2022 走看看