Redis第二话 -- Redis的高端操作(发布订阅、事务、LUA脚本)

12 篇文章 1 订阅
5 篇文章 0 订阅

在Redis中还有一些用法是我们在工作中用的比较少的,本文统一整理一下。

1.发布订阅

在Redis里面除了List的阻塞队列可以实现消息队列以外,还有一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。命令:psubscribe subscribe/publish
普通的发布订阅队列:

#订阅某个队列
redis01:6379> subscribe chat
Reading messages... (press Ctrl-C to quit)

#发布消息
redis01:6379> publish chat hello
(integer) 1

支持正则的发布订阅:

#订阅以book开头的 和 以video结尾的消息队列
redis01:6379> psubscribe book* *video
Reading messages... (press Ctrl-C to quit)

#发送到对应队列
redis01:6379> PUBLISH hot_video hello
(integer) 1
redis01:6379> PUBLISH book hello
(integer) 1

Redis的消息通信为实时通信,没有持久化功能。在工作中没什么太大的使用,但在Redis内部中简直是为主从量身定做。

2.Lua脚本

2.1 redis脚本

Redis脚本是使用Lua解释器来执行脚本。执行脚本的命令是eval。语法如下

# 引号内为脚本内容 数字3表示几个key key和value是以组的形式存在的
redis01:6379> eval "return KEYS[2]" 3 key1 key2 key3 v1 v2 v3
"key2"
#返回多个值的用法
redis01:6379> eval "return {KEYS[2],ARGV[3]}" 3 key1 key2 key3 v1 v2 v3
1) "key2"
2) "v3"

在lua中调用redis执行命令则使用redis.call
现有这样一个需求:set某个key时如果存在则返回该key的值,如果不存在则set后返回0

#test.lua 语法不熟悉的可以先去菜鸟看看
local v=redis.call('get',KEYS[1])
if v then
    return v
else
    redis.call('set',KEYS[1],ARGV[1])
    return 0
end

在服务器执行一下:

root@88a0785be648:/data# redis-cli --eval test.lua test5 , world5  
(integer) 0
root@88a0785be648:/data# redis-cli --eval test.lua test5 , world5
"world5"

一定要注意逗号前后的空格,不然会报语法错误!

2.2 脚本缓存

在redis中还提供了脚本缓存,可以给通用的脚本进行缓存生成hash码,然后可以直接通过hash码执行脚本。命令:script load/evalsha

127.0.0.1:6379> script load "local v=redis.call('get',KEYS[1]); if v then return v else redis.call('set',KEYS[1],ARGV[1]) return 0 end"
"7c5ee472b88998b37e1e4b7e9d024a60040497a5"
127.0.0.1:6379> evalsha "7c5ee472b88998b37e1e4b7e9d024a60040497a5" 1 key1 value1
(integer) 0
127.0.0.1:6379> evalsha "7c5ee472b88998b37e1e4b7e9d024a60040497a5" 1 key1 value2
"value1"

2.3 脚本原子性测试

准备脚本:

redis.call('set','date','0501')
redis.call('hset','date','a','b')

既然是保证原子性的,那如果执行脚本出错,会正常回滚吗?

root@88a0785be648:/data# redis-cli --eval automic.lua test5 , world5
(error) ERR Error running script (call to f_a8f81a7e212ef0f67e2fe7cdba3b028bde50bd83): @user_script:2: WRONGTYPE Operation against a key holding the wrong kind of value 
root@88a0785be648:/data# redis-cli 
127.0.0.1:6379> get date
"0501"
127.0.0.1:6379> hgetall date
(error) WRONGTYPE Operation against a key holding the wrong kind of value

结果是不会的!

2.4 脚本死循环测试,是否阻塞其他客户端?

#执行一个while死循环 然后其他窗口再进行set操作,排他测试
root@88a0785be648:/data# redis-cli 
127.0.0.1:6379> eval "while(true) do end" 0


#接下来打开另外一个客户端窗口
root@88a0785be648:/data# redis-cli
127.0.0.1:6379> get hello
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

这样就能解释为什么lua脚本一定时能保证原子性的了,一旦执行lua脚本会给其他的客户端抛出BUSY

3.redis事务

redis有提供事务功能,原理是将开启事务后的指令统一丢到队列中执行,命令为multi exec/discard

root@88a0785be648:/data# redis-cli 
127.0.0.1:6379> set a 100
OK
127.0.0.1:6379> set b 100
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby a 10
QUEUED
127.0.0.1:6379> decrby b 10
QUEUED
127.0.0.1:6379> exec
1) (integer) 110
2) (integer) 90

在此之外,做了一个这样的测试
在A窗口开启了事务,做了以上操作,但并未提交,接下来在B窗口做了递增操作,最后的结果如下:

#在开启事务以后 在B窗口执行以下命令
root@88a0785be648:/data# redis-cli 
127.0.0.1:6379> get a
"100"
127.0.0.1:6379> incrby a 20
(integer) 120

#最终A窗口中结果
127.0.0.1:6379> exec
1) (integer) 130
2) (integer) 90

也就是说redis事务并不具备排它性,注意谨慎使用!

以上就是本章的全部内容了!

上一篇:Redis第一话 – Redis介绍以及基于Docker安装Redis
下一篇:Redis第三话 – Springboot集成Redis以及常用API和客户端介绍)

不见只今汾水上,唯有年年秋雁飞

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值