文章目录
- 一 发布/订阅命令
- 1.1 消息系统
- 1.2 subscribe
- 1.3 psubscribe
- 1.4 publish
- 1.5 unsubscribe
- 1.6 punsubscribe
- 1.7 pubsub
- 1.7.1 pubsub channels
- 1.7.2 pubsub numsub
- 1.7.3 pubsub numpat
- 二 Redis 事务
- 2.1 Redis 事务特性
- Redis 事务实现
- 2.1.1 三个命令
- 2.1.2 基本使用
- 2.2. Redis 事务异常处理
- 2.2.1 语法错误
- 2.2.2 执行异常
- 2.3 Redis 事务隔离机制
- 2.3.1 需要事务隔离机制的原因
- 2.3.2 隔离的实现
- 2.3.3 隔离的实现原理
一 发布/订阅命令
1.1 消息系统
- 发布/订阅(pub/sub),是一种消息通信模式:发布者【消息生产者】,生产和发送消息到存储系统;订阅者【消息消费者】,从存储系统接收和消费消息。这个存储系统可以是文件系统 FS、消息中间件 MQ、数据管理系统 DBMS,也可以是 Redis。 整个消息发布者、订阅者与存储系统称为消息系统。
- 消息系统中的订阅者订阅了某类消息后,只要存储系统中存在该类消息,其就可不断的接收并消费这些消息。当存储系统中没有该消息后,订阅者的接收、消费阻塞。而当发布者将消息写入到存储系统后,会立即唤醒订阅者。当存储系统放满时,不同的发布者具有不同的处理方式:有的会阻塞发布者的发布,等待可用的存储空间;有的则会将多余的消息丢失。
- 不同的消息系统消息的发布/订阅方式也是不同的。例如 RocketMQ、Kafka 等消息中间件构成的消息系统中,发布/订阅的消息都是以主题 Topic 分类的。而 Redis 构成的消息系统中,发布/订阅的消息都是以频道 Channel 分类的。
1.2 subscribe
- 格式:
SUBSCRIBE channel *channel …+
- 功能:Redis 客户端通过一个
subscribe
命令可以同时订阅任意数量的频道。在输出了订阅主题后,命令处于阻塞状态,等待相关频道的消息。
1.3 psubscribe
- 格式:
PSUBSCRIBE pattern *pattern …+
- 功能:订阅一个或多个符合给定模式的频道
- 说明:这里的模式只能使用通配符
*
。 如,it*
可以匹配所有以 it 开头的频道,如it.news、it.blog、it.tweets
;news.*
可以匹配所有以news.
开头的频道。
1.4 publish
- 格式:
PUBLISH channel message
- 功能:Redis 客户端通过一条 publish 命令可以发布一个频道的消息,返回值为接收到该消息的订阅者数量。
1.5 unsubscribe
- 格式:
UNSUBSCRIBE *channel *channel …++
- 功能:Redis 客户端退订指定的频道
- 说明:如果没有频道被指定,会执行一个无参数的
unsubscribe
命令,客户端使用SUBSCRIBE
命令订阅的所有频道都会被退订。在这种情况下,返回一个信息,告知客户端所有被退订的频道
1.6 punsubscribe
- 格式:
PUNSUBSCRIBE *pattern *pattern …++
- 功能:退订一个或多个符合给定模式的频道
- 说明:这里的模式只能使用通配符
*
。如果没有频道被指定,其效果与 SUBSCRIBE 命令相同,客户端将退订所有订阅的频道。
1.7 pubsub
- 格式:
PUBSUB <subcommand> [argument *argument …++
- 功能:
PUBSUB
是一个查看订阅与发布系统状态的内省命令集,它由数个不同格式的子命令组成
1.7.1 pubsub channels
- 格式:
PUBSUB CHANNELS [pattern]
- 功能:列出当前所有的活跃频道。活跃频道指的是那些至少有一个订阅者的频道。
- 说明:pattern 参数是可选的。如果不给出 pattern 参数,将会列出订阅/发布系统中的所有活跃频道。如果给出 pattern 参数,那么只列出和给定模式 pattern 相匹配的那些活跃频道。pattern 中只能使用通配符*。
1.7.2 pubsub numsub
- 格式:
PUBSUB NUMSUB [channel-1 … channel-N]
- 功能:返回给定频道的订阅者数量。不给定任何频道则返回一个空列表。
1.7.3 pubsub numpat
- 格式:
PUBSUB NUMPAT
- 功能:查询当前 Redis 所有客户端订阅的所有频道模式的数量总和
二 Redis 事务
- Redis 的事务的本质是一组命令的批处理。这组命令在执行过程中会被顺序地、一次性全部执行完毕,只要没有出现语法错误,这组命令在执行期间是不会被中断。
2.1 Redis 事务特性
- Redis 的事务仅保证数据的一致性,不具有像 DBMS 一样的 ACID 特性。
- 不具备原子性:这组命令中的某些命令的执行失败不会影响其它命令的执行,不会引发回滚。
- 没有复杂的隔离级别:这组命令通过乐观锁机制实现了简单的隔离性。
- 与事务无关:这组命令的执行结果是被写入到内存的,是否持久取决于 Redis 的持久化策略。
Redis 事务实现
2.1.1 三个命令
- Redis 事务通过三个命令进行控制。
- muti:开启事务
- exec:执行事务
- discard:取消事务
2.1.2 基本使用
- 下面是定义并执行事务的用法:事务执行后,再访问事务中定义的变量,其值是修改过后。
- 下面是定义但取消事务的举例:事务取消后,事务中的命令是没有执行的
2.2. Redis 事务异常处理
2.2.1 语法错误
- 当事务中的命令出现语法错误时,整个事务在 exec 执行时会被取消
2.2.2 执行异常
- 如果事务中的命令没有语法错误,但在执行过程中出现异常,该异常不会影响其它命令的执行。
2.3 Redis 事务隔离机制
2.3.1 需要事务隔离机制的原因
- 在并发场景下可能会出现多个客户端对同一个数据进行修改的情况,造成数据不一致的情况!
- 如:有两个客户端 C 左与 C 右,C 左需要申请 40 个资源,C 右需要申请 30 个资源。它们首先查看了当前拥有的资源数量,即 resources 的值为50,都感觉资源数量可以满足自己的需求,于是修改资源数量,以占有资源。但结果却是资源出现了“超卖”情况。
- 如:有两个客户端 C 左与 C 右,C 左需要申请 40 个资源,C 右需要申请 30 个资源。它们首先查看了当前拥有的资源数量,即 resources 的值为50,都感觉资源数量可以满足自己的需求,于是修改资源数量,以占有资源。但结果却是资源出现了“超卖”情况。
- Redis 事务通过乐观锁机制实现了多线程下的执行隔离
2.3.2 隔离的实现
- Redis 通过 watch 命令再配合事务实现了多线程下的执行隔离
- 以上两个客户端执行的时间顺序为:
2.3.3 隔离的实现原理
- 当某一客户端对 key 执行了 watch 后,系统就会为该 key 添加一个 version 乐观锁,并初始化 version。例如初值为 1.0。
- 此后客户端 C 左将对该 key 的修改语句写入到了事务命令队列中,虽未执行,但其将该key 的 value 值与 version 进行了读取并保存到了当前客户端缓存。此时读取并保存的是version 的初值 1.0。
- 此后客户端 C 右对该 key 的值进行了修改,这个修改不仅修改了 key 的 value 本身,同时也增加了 version 的值,例如使其 version 变为了 2.0,并将该 version 记录到了该 key信息中。
- 此后客户端 C 左执行 exec,开始执行事务中的命令。不过,其在执行到对该 key 进行修改的命令时,该命令首先对当前客户端缓存中保存的 version 值与当前 key 信息中的version 值。如果缓存 version 小于 key 的 version,则说明客户端缓存的 key 的 value 已经过时,该写操作如果执行可能会破坏数据的一致性。所以该写操作不执行