1-Redis - 进阶操作
before
centos7.9 + redis3.0.2
本篇来研究Redis中稍微进阶的操作,如事务、发布订阅模式这些。
发布订阅
Redis中的发布订阅模式,类似于RabbitMQ中的主题订阅模式。
在发布订阅模式中,有三个角色:
- 生产者,它同样是一个redis客户端,负责生产消息。可以有一个或者多个生产者往一个或者多个频道中发布消息。
- channel,频道,类似于主题,接收来自于生产者产生的消息,为不同的消息打个标签,可以有多个频道。它维护在redis的server中。
- 消费者,也是一个Redis客户端,可以订阅感兴趣的消息,也就是可以(一个或者多个)订阅不同频道的消息。
如上图,有三个生产者负责生产消息,并且发布到指定的频道中,消费者可以选择订阅一个或者多个频道中的消息。
操作:
# 首先要现有多个消费者订阅不同的频道,订阅一个、多个、模糊匹配;你可以起三个终端来分别订阅
SUBSCRIBE fm103
SUBSCRIBE fm103 fm104
PSUBSCRIBE fm*
# 然后有不同的生产者往指定的频道生产消息,可以用三个终端来发布消息,也可以用一个也行,分别向不同的频道发布消息
PUBLISH fm103 "fm103.9 it's my radio, messages1"
PUBLISH fm103 "fm103.9 it's my radio, messages2"
PUBLISH fm103 "fm103.9 it's my radio, messages3"
PUBLISH fm104 "fm104.9 it's my radio, messages1"
PUBLISH fm104 "fm104.9 it's my radio, messages2"
PUBLISH fm104 "fm104.9 it's my radio, messages3"
PUBLISH fm105 "fm105.9 it's my radio, messages1"
PUBLISH fm105 "fm105.9 it's my radio, messages2"
PUBLISH fm105 "fm105.9 it's my radio, messages3"
上述几个消费者就能根据自己的规则接收到不同的频道的消息,其效果如下图:
这里需要注意的是,消费者不会接收频道的历史消息,只会接受订阅后产生的消息。
其他操作:
# 取消订阅频道
UNSUBSCRIBE # 不跟频道号则取消订阅所有频道
UNSUBSCRIBE fm103 # 取消订阅fm103频道
UNSUBSCRIBE fm103 fm104 # 取消订阅多个频道
UNSUBSCRIBE fm* # 取消订阅以fm开头的频道
Redis发布订阅优点:
- Redis的发布订阅支持多个生产者/消费者,同时生产消费消息,优点就是非常简洁,因为它的实现原理就是单纯地为生产者、消费者建立「数据转发通道」,把符合规则的数据,从一端转发到另一端。
缺点:
- 发布订阅模式是"发后既忘"的工作模式,如果订阅者离线,那么重连之后不能消费之前的历史消息。
- 无法持久化保存消息,如果 Redis 服务器宕机或重启,那么所有的消息将会丢失。
- 积压的消息不能太多,否则也会丢数据【可以通过client-output-buffer-limit这个参数配置】。
小结:对于特别简单的发布订阅场景,可以考虑Redis的发布订阅功能,但是复杂的场景,还是选择专业的消息队列软件实现吧。
事务
Redis的事务是基于队列实现的;而MySQL的事务是基于事务日志和锁机制实现;Redis是乐观锁机制。
Redis使用mulit
开启事务,即事务中的修改操作都会push到队列中,此时并没有执行修改命令,什么时候提交事务呢?当你exec
时,Redis会将事务队列中的命令操作都一一的执行一边;所以Redis的回滚也非常的简单,直接把事务队列删除就完了,命令是discard
。
操作:
# 正常开启并提交事务的示例
127.0.0.1:6379> MULTI # 使用multi开启事务
OK
127.0.0.1:6379> set t1 a # 操作,返回了queued,说明把操作放到了事务队列中了
QUEUED
127.0.0.1:6379> set t1 b
QUEUED
127.0.0.1:6379> EXEC # 提交事务
1) OK
2) OK
127.0.0.1:6379> get t1 # 结果正常
"b"
# 回滚的事务示例
127.0.0.1:6379> EXISTS t2 # 首先,key不存在
(integer) 0
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> set t2 a
QUEUED
127.0.0.1:6379> DISCARD # 回滚,即把事务队列删除
OK
127.0.0.1:6379> EXISTS t2 # 可以看到事务没有执行成功,key仍然不存在
(integer) 0
由于Redis是乐观锁机制,所以它允许多个客户端同时操作同一个key,这点和MySQL不一样,MySQL遇到这种情况,当一个客户端在修改一条记录时,会加个锁,别的客户端要修改这条记录,会遇到锁等待。
那Redis是如何处理这种情况呢?答案非常简单,哪个事务先提交就以哪个事务为准。
使用Redis的事务模拟抢票的示例,第一回合:
可以看到,应该当余票为0时,就不能在下单成功了!所以,要完善这个示例,就要用到新的命令watch
,该命令用于监控指定的key,如果在事务执行之前,检测到这个key被别的事务所修改,那么就会终止当前事务的执行,即让当前事务执行失败。
第二回合:
欢迎指正,that's all,see also: