在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和客户端介绍)
不见只今汾水上,唯有年年秋雁飞