zoukankan      html  css  js  c++  java
  • Redis原理(一)

    发布订阅模式

    列表的局限

      通过队列的rpush和blpop可以实现消息队列(队尾进队头出),没有任何元素可以弹出的时候,连接会被阻塞。
      但是基于list实现的消息队列,不支持一对多的消息分发,相当于只有一个消费者。
      如果要实现一对多的消息分发,怎么办?
    

    发布订阅模式

      除了通过list实现消息队列之外,Redis还提供了发布订阅的功能。
    

    订阅频道

      消息的生产者和消费者是不同的客户端,连接到同一个Redis的服务。通过什么对象把生产者和消费者关联起来呢?
        在RabbitMQ里面叫Queue,在Kafka里面叫Topico Redis的模型里面这个叫
    

    channel (频道)。
    订阅者可以订阅一个或者多个channeL消息的发布者可以给指定的channel发布消息。只要有消息到达了 channel,所有订阅了这个channel的订阅者都会收到这条消息。

    订阅者订阅频道:可以一次订阅多个,比如这个客户端订阅了 3个频道,频道不用实现创建。

    subscribe channel-1 channel-2 channel-3
    

    发布者可以向指定频道发布消息(并不支持一次向多个频道发送消息):

    publish channel-1 2673
    

    取消订阅(不能在订阅状态下使用):

    unsubscribe channel-1
    

    按规则(Pattern)订阅频道

    支持?和占位符。?代表一个字符,代表0个或者多个字符。
    例如,现在有三个新闻频道,运动新闻(news-sport)、音乐新闻(news-music)、天气新闻(news-weather) 。
    三个消费者,消费端1,关注运动信息:

    psubscribe * sport
    

    消费端2,关注所有新闻:

    psubscribe news*
    

    消费端3,关注天气新闻:

    psubscribe news-weather
    

    生产者,向3个频道发布3条信息,对应的订阅者能收到消息:

    publish news-sport kobe
    publish news-music jaychou
    publish news-weather sunny
    

    一般来说,考虑到性能和持久化的因素,不建议使用Redis的发布订阅功能来实现
    MQ。Redis的一些内部机制用到了发布订阅功能。

    Redis事务

    为什么要用事务呢

    Redis的单个命令是原子性的(比如get set mget mset) , 要么成功要么失败,不 存在并发干扰的问题。

    如果涉及到多个命令的时候,需要把多个命令作为一个不可分割的处理序列,就必 须要依赖Redis的功能特性来实现了。

    Redis提供了事务的功能,可以把一组命令一起执行。Redis的事务有3个特点:

    1、按进入队列的倾序执行。
    2、 不会受到其他客户端的请求的影响。
    3、 事务不能嵌套,多个multi命令效果一样

    事务的用法

    Redis的事务涉及到四个命令:multi (开启事务),exec (执行事务),discard
    (取消事务),watch (监视)。
    案例场景:xiami和xiaoming各有1000元,xiami向xiaoming转账100元。

    set xiami 1000 
    set xiaoming 1000 
    multi 
    decrby xiami 100 
    incrby xiaoming 100 
    exec
    get xiami
    get xiaoming
    

    通过multi的命令开启事务。multi执行后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中。当exec命令被调用时,所有队列中的命令才会被执行。
    如果没有执行exec,所有的命令都不会被执行。
    QA:如果中途不想执行事务了,怎么办?
    可以调用discard可以清空事务队列,放弃执行。

    multi
    decrby xiami 100 
    discard
    get xiami
    

    watch命令

    为了防止事务过程中某个key的值被其他客户端请求修改,带来非预期的结果,在 Redis中还提供了一个watch命令。
    也就是多个客户端更新变量的时候,会跟原值做比较,只有它没有被其他线程修改 的情况下,才更新成新的值。它可以为Redis事务提供CAS乐观锁行为(Compare and Swap) 。

    我们可以用watch监视一个或者多个key,如果开启事务之后,至少有一个被监视 key键在exec执行之前被修改了,那么整个事务都会被取消(key提前过期除外)。可以用unwatch取消。

    client1 client2
    set balance
    watch balance
    multi
    incrby balance 100
    decrby balance 100
    exec【返回nil】
    get balance

    以上是事务相关的命令的基本使用方法。如果在执行时候的时候发生了异常,而不是我们主动取消,会发生什么情况呢?

    事务可能遇到的问题

    事务执行遇到的问题分成两种,一种是在执行exec之前发生错误,一种是在执行 exec之后发生错误。

    在执行exec之前发生错误

    比如:入队的命令存在语法错误,包括参数数量,参数名等等(编译器错误)。

    multi
    set snail 2673
    set hehe yes
    hset bobo 666
    exec
    

    比如这里出现了参数个数错误,事务会被拒绝执行,也就是队列中所有的命令都不会得到执行。

    在执行exec之后发生错误

    比如对String使用了 Hash的命令,参数个数正确,但数据类型错误,这是一种运 行时错误。

    flushall
    multi
    set k1 1
    hset k1 a b
    exec
    1)OK
    2)(error) WRONGTYPE Operation against a key holding the wrong kind of value 
    get k1
    

    最后我们发现set k1 1的命令是成功的,也就是在这种发生了运行时异常的情况下,
    只有错误的命令没有被执行,但是其他命令没有受到影响。

    这个显然不符合我们对原子性的定义。也就是我们没办法用Redis的这种事务机制 来实现原子性,保证数据的一致。

    那么为什么不回滚呢?

    官方的解释是这样的:

    • Redis命令只会因为错误的语法而失败,也就是说,从实用性的角度来说,失败的命令是由代码错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中(这个是程序员的锅)。
    • 因为不需要对回滚进行支持,所以Redis的内部可以保持简单且快速。需要知道的是:回滚不能解决代码的问题(程序员的锅必须程序员来背)。

    Redis从2.6版本开始引入了 Lua脚本,也就是说Redis可以用Lua来执行Red is 命令。

  • 相关阅读:
    python标准库之MultiProcessing库的研究 (1)
    python标准库Beautiful Soup与MongoDb爬喜马拉雅电台的总结
    ASP.NET平台下从浏览器地址栏输入之后发生的事
    async & await 异步编程的一点巧方法
    【MySQL】MySQL一主二从复制环境切换主从库
    【MySQL】MySQL基于二进制文件安装
    【MySQL】MySQL半同步复制
    【MySQL】MySQL搭建主主复制(双主复制/DUAL Master)环境
    【MySQL】MySQL复制
    【MySQL】MySQL搭建主从复制环境
  • 原文地址:https://www.cnblogs.com/snail-gao/p/14305912.html
Copyright © 2011-2022 走看看