• 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作者认为这种情况是存在编译错误,实际生产中不会出现?)
  • 相关阅读:
    开发中常用的JS知识点集锦
    浏览器音频兼容和ffmpeg的音频转码使用
    web页面和小程序页面实现瀑布流效果
    微信小程序之支付密码输入demo
    Mac安装nginx配置过程
    前端工具mock的使用
    汇编语言学习
    Swift学习笔记
    如何快速融入团队并成为团队核心(四)
    如何快速融入团队并成为团队核心(三)
  • 原文地址:https://www.cnblogs.com/xiang9286/p/11003922.html
Copyright © 2020-2023  润新知