Redis 中发布订阅和事务命令介绍

165 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情

发布订阅

Redis 中发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接受消息。发送者(pub)不是计划发送消息给特定的订阅者(sub),而是将发布的消息分到不同的频道(channel),不需要知道具体的订阅者是谁。同样订阅者(sub)只需要去频道(channel)获取自己想要的消息即可,不需要知道这个消息来源于哪个发布者(pub)。这是不是就让发布者(pub)和订阅者(sub)高度解耦了。下面我们就一起来看看它到底是如何实现的。

Redis 发布订阅(pub/sub)简单的实现原理图如下:

图片描述

实际项目开发中,我们有时候会基于 Redis 的这种发布订阅的特性,完成像上图中的多组生产者、多组消费者的场景。它实际上可以看作一种队列模型,生产者(pub)生产消息发给频道(channel),消费者(sub)从频道(channel)获取消息进行消费。这里一个消息可以被一个消费者消费,也可以被多个消费者消费。

➡️ 一个生产者,一个消费者

  • 开启一个客户端作为消费者(sub)订阅消息,使用 subscribe 命令:

          subscribe q_channel
    

    ✨ 说明:subscribe 是用来订阅给定的一个或多个频道的信息。上面订阅频道的名称为 q_channel

  • 重新开启一个新客户端作为生产者(pub)生产消息,使用 publish 命令:

    📖 需求:使用 publish 命令将生产的 hello,redis 和 hello,lanqiao 者两个消息发送到频道 q_channel 中,让消费者进行订阅。

    publish q_channel hello,redis
    

    ✨ 说明:pushlish 是用来将消息发送给指定的频道,返回接收到信息的订阅者的数量。

    从上图中可以发现:

    • 开启两个客户端,一个消费者(使用 subscribe 命令),一个生产者(使用 publish 命令)。
    • 在生产者分别生产了 hello,redis 和 hello,lanqiao 这两条消息,并且将这两条消息传递给了频道 q_channel
    • 消费者通过订阅频道 q_channel ,消费了这两条消息。

➡️ 一个生产者,多个消费者

  • 开启两个客户端作为消费者(一个使用 subscribe 命令订阅,一个使用 psubscribe 命令订阅):

    subscribe:用来订阅给定的一个或多个频道的信息。

     subscribe ch_queue1
    

    ✨ 说明:这里用来订阅 ch_queue1 频道的消息。

    subscribe:用来订阅一个或多个符合给定模式的频道。

    psubscribe ch*
    

    ✨ 说明:这里用来订阅所有符合 ch 开头的频道的消息。

  • 开启一个客户端作为生产者,使用 publish 命令发布消息:

    📖 需求:使用 publish 命令将生产的 hello,lan 发送到频道 ch_queue1 ,将生产的 hello,redis 发送到频道 ch_queue2 ,将生产的 hello,java 发送到频道 ch_queue3 ,让这些消息被消费者进行订阅。

    publish ch_queue1 hello,lan
    publish ch_queue2 hello,redis
    publish ch_queue3 hello,java
    
    

    ✨ 说明:ch_queue1 频道返回值为 2,说明被两个订阅者订阅了,其余的都是被一个订阅者订阅了。

    从上图中可以发现:

    • 开启三个客户端,两个消费者(分别使用 subscribe 和 psubscribe 命令订阅),一个生产者(使用 publish 命令)。
    • 在生产者分别生产了 hello,lan 、 hello,redis 和 hello,java这三条消息,并且将这三条消息分别传递给了频道 ch_queue1、 ch_queue2 和 ch_queue3
    • 其中 hello,lan 被两个订阅者消费了(ch_queue1 和 ch*)。

➡️ 发布订阅的其他相关命令

  • pubsub subcommand [argument [argument ...]]:查看订阅和发布系统状态。

  • punsubscribe [pattern [pattern ...]]:用于退订所有给定模式的频道。

  • unsubscribe channel [channel ...]:用于退订给定的一个或多个频道。

事务

Redis 事务可以一次执行多个命令,并且带有以下三个重要保证:

  • 批量操作在发送 exec 命令前被放入队列缓存。
  • 收到 exec 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行主要经历以下三个阶段:开始事务、命令入队、执行事务。Redis 事务相关的操作命令主要是有:discardexecmultiunwatchwatch。接下来我们就一起结合这些命令来学习 Redis 事务。

➡️ Redis 事务相关操作命令

  • multi 和 execmulti 用于开启一个事务,它总是返回 OKexec 用于执行事务块内所有的命令,返回的结果是事务块内所有的命令的返回值,按照命令执行的先后顺序排列。

    redis-cli --raw
    multi
    set age 40
    QUEUED
    incr age
    QUEUED
    exec
    get age
    
    

    ✨ 说明:当执行 multi 执行之后,客户端可以继续向服务端发送任意多条命了,但这些命令都不会立即执行,而是被放到队列中。当执行 exec 命令后,所有队列中的命令才会被执行。

  • discard:取消事务,放弃执行事务块内的所有命令,它总是返回 OK

    ✨ 说明:当执行 discard 命令后,返回 OK。此时事务会被放弃,事务队列会被清空,并且客户端会从事务状态中推出。

  • watch:用于监视一个或多个 key ,如果在事务执行之前 key 被其他命令所改动,那么事务将被打断。

  • unwatch:用于取消 watch 命令对所有 key 的监视。

➡️ 事务中的错误

  • 事务在执行 exec 之前,入队的命令可能会出错(全体遭殃)。

    ✨ 说明:从 Redis 2.6.5 开始,服务器会对命令入队失败的情况进行记录,并在客户端调用 exec 命令时,拒绝执行并自动放弃这个事务。

  • 命令可能在 exec 调用之后失败(冤头债主)。

    ✨ 说明:在 exec 命令执行之后所产生的错误, 没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误, 事务中的其他命令仍然会继续执行。