这是我参与8月更文挑战的第 7 天,活动详情查看:8月更文挑战
本章重点介绍 redis 的高级应用,如应用于要求不是太严格的基于 发布-订阅模型 的消息队列;如对多条 redis 指令进行事务化管理以提高效率
我的 redis 系列,感兴趣的朋友也可以来看看
发布-订阅模式
就好比我们大家都关注了一个微信订阅号,订阅号他一更新文章,就自动向所有关注了他的人去推送这篇文章
相关命令
pubsub subcommand [argument [argument ...]]
查看订阅与发布系统状态
127.0.0.1:16379> pubsub channels
(empty list or set)
subscribe channel [channel ...]
订阅给定的一个或多个频道的信息
127.0.0.1:16379> SUBSCRIBE channel-1
Reading messages... (press Ctrl-C to quit)
1) "subscribe" # 指令
2) "channel-1" # 订阅的频道
3) (integer) 1 # 成功订阅的数量
psubscribe pattern [pattern ...]
订阅一个或多个符合给定模式的频道
pattern 规则详解
? 占位符,必定匹配一个字符
* 通配符,匹配 0-n 个任意字符
消息订阅端
订阅满足表达式为 a?a, b*b 的频道
127.0.0.1:16379> PSUBSCRIBE a?a b*b
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "a?a"
3) (integer) 1
1) "psubscribe"
2) "b*b"
3) (integer) 2
消息推送端
127.0.0.1:16379> publish aa 'hello'
(integer) 0
127.0.0.1:16379> publish aba 'hello'
(integer) 1
127.0.0.1:16379> publish abba 'hello'
(integer) 0
127.0.0.1:16379> publish bb 'hello'
(integer) 1
127.0.0.1:16379> publish bab 'hello'
(integer) 1
127.0.0.1:16379> publish baab 'hello'
(integer) 1
订阅端收到推送的消息
1) "pmessage" # 消息类型
2) "a?a" # 匹配的频道规则
3) "aba" # 实际匹配的频道
4) "hello" # 收到的消息
---------------------------------
1) "pmessage"
2) "b*b"
3) "bb"
4) "hello"
---------------------------------
1) "pmessage"
2) "b*b"
3) "bab"
4) "hello"
---------------------------------
1) "pmessage"
2) "b*b"
3) "baab"
4) "hello"
publish channel message
将信息发送到指定的频道
# 消息推送端,像管道 channel-2 发送消息,推送失败,暂时没有客户端监听此管道
127.0.0.1:16379> PUBLISH channel-2 hello
(integer) 0
---------------------------
# 消息推送端,像管道 channel-1 发送消息,推送成功
127.0.0.1:16379> PUBLISH channel-1 hello
(integer) 1
# 监听管道的客户端,收到消息
127.0.0.1:16379> SUBSCRIBE channel-1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel-1"
3) (integer) 1
1) "message"
2) "channel-1"
3) "hello"
unsubscribe [channel [channel ...]
退订所有给定模式的频道
punsubscribe [pattern [pattern ...]]
退订所有给定模式的频道
备注:如果尝试向一个未被监听的频道发送消息,会返回 0,表示 false
相关的命令可以总结为:
客户端订阅具体的频道 (subscribe),或者根据表达式去订阅(psubscribe)
客户端向具体的频道发送单条消息
客户端退订具体的频道,或者是根据表达式去退订频道
示例代码总结
使用场景
最常见的使用场景就是替代 MQ
这里的替代是指,不接受引入 MQ 后系统的会变得更加复杂,不太好维护,并且,对消息的接收消费要求不严格,能容许消息丢失的风险,毕竟,MQ都会提供消息确认送达的机制来保证每一条信息都会被消费
这里,来和主流的 RabbitMQ 做对比:
- redis: 轻量级,低延迟,高并发,低可靠性;
- rabbitmq:重量级,高可靠,异步,不保证实时;
Redis 事务
介绍
或许很多人第一反应,redis 的事务,应该会和 spring 的事务是差不多的一个玩意,实际上,完全不同
redis 的事务,或者更加符合实际的描述,可以理解为 redis 命令的批量执行,具备如下特性:
- 被事务包含的命令,会顺序执行,并且不会被其他客户端的命令打断
- 事务命令在执行的过程中,假定中间有一条执行失败,不会中断事务,已经执行的命令不会回滚,后续的命令仍然会继续执行
一个标准事务的执行流程:
- 开启事务(使用指令 MULTI)
- 命令入队
- 输入 redis 的操作命令,不会立即执行,会先压入队列当中
- 可选操作,放弃当前事务,清空事务队列,使用指令 DISCARD
- 执行事务(使用指令 EXEC)
注:redis 的事务成功或者失败的点,只会依赖与 exec 指令是否执行
相关命令
multi
标记一个事务块的开始
exec
执行所有事务块内的命令
discard
取消事务,放弃执行事务块内的所有命令
watch key [key...]
监听1个或多个key,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
摘抄自 官方文档
为什么Redis不支持回滚?
如果你了解关系数据库,那么Redis事务处理机制看起来有点奇怪。
Redis事务中的命令允许失败,但是Redis会继续执行其它的命令而不是回滚所有命令。
这么做的原因有两点:
- Redis 命令只在两种情况失败:
- 语法错误的时候才失败(在命令输入的时候不检查语法)。
- 要执行的key数据类型不匹配:这种错误实际上是编程错误,这应该在开发阶段被测试出来,而不是生产上。
- 因为不需要回滚,所以Redis内部实现简单并高效。
当出现bug的时候Redis的这种做法并不友好,可是需要注意的是回滚并不能解决程序bug。
例如,对于需要增加1的逻辑增加了2,或者操作的key类型不对,这些情况回滚并没有什么帮助。
考虑到没有人能避免程序员错误,并且这种错误也基本不能进入生产环境,我们选择了更简单且更高效的方法,不支持错误回滚。