一、Redis 消息通信模式 -- 发布/订阅
Publish/Subscribe 是 Redis 的消息通信模式,某些 redis client 可以去订阅某些channel
,然后可以启用某些 redis client 向某些chenel发布message
,然后message就会被推送
给那些申请订阅的客户端,这个过程就像是预定/上门配送光明牛奶
的过程。
举个栗子来说明过程:
- Step 1. 某客户端A订阅了channel1
127.0.0.1:6379[1]> subscribe channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
- Step 2. 新开一个客户端B,向channel1中发布message
$ redis-cli
127.0.0.1:6379> auth root
OK
127.0.0.1:6379> publish channel1 "hello redis"
(integer) 1
127.0.0.1:6379> publish channel1 "hello beijing"
(integer) 1
观察A中可以看到有消息收到:
127.0.0.1:6379[1]> subscribe channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
1) "message"
2) "channel1"
3) "hello redis"
1) "message"
2) "channel1"
3) "hello beijing"
二、Redis 事务
同 MySQL 等数据库类似,内存数据库 Rediscove(NoSQL)同样有事务的概念。Redis 开启一个事务后,输入多个命令,然后一起提交执行。
- Redis 命令具有原子性;
- Redis 事务不具有原子性:Redis 事务可以看成是多个任务组成的脚本,当某个命令执行失败时并不会影响到之前已被执行的命令(即不会回滚)。
Redis 事务的相关命令:
命令 | 描述 | 备注 |
---|---|---|
multi | 事务开启 | 无 |
exec | 执行事务 | 执行 multi 之后 exec 之前输入的所有命令 |
discard | 取消事务 | 用在multi 之后 exec 之前,当然输入的命令也不会执行 |
watch | 监听一个或多个key | 用在 multi 之前。比如如果client A 中 watch k1 并使用 multi 启动事务且输入命令 get k1,但是在 client A 使用 exec 执行事务之前 client B 更新了 k1 的值,那么之后client A 执行事务会报 nil,即事务被打断 |
unwatch | 取消监听 | 无 |
下面举例说明 watch 的使用:
- 首先,client A 中定义事务:
127.0.0.1:6379> watch k1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> get k2
QUEUED
- 然后,client B 中修改 k1 的值:
127.0.0.1:6379> set k1 v2
OK
- 最后,client A 中执行事务:
127.0.0.1:6379> exec
(nil)
可以看到,client A 中的事务被打断了。
三、Redis 脚本
Redis 中内嵌了Lua 解释器
用于解释执行 script。
3.1 为何引入脚本?
-
减少网络开销:
Lua 脚本将多个命令请求通过脚本的形式一次性发送; -
原子性保证:
与 Redis 事务无法保证原子性不同,脚本能够保证所有命令的原子性; -
可复用:
客户端发送的脚本会被永久保存在 Redis 中,供后期复用。
3.2 使用方法
3.2.1 redis-cli 中使用
Redis 中通常使用eval 命令
来提交脚本。命令格式为:
eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 k1 k2 v v2
其中:
- eval为命令;
- "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 是脚本,其中
{}就相当于数组
; - KEYS[1]:表示第1个参数,对应后面的k1;
- KEYS[2]:表示第2个参数,对应后面的k2;
- ARGV[1]:表示第1个value,对应后面的v1;
- ARGV[2]:表示第1个value,对应后面的v2;
- 2 是key的个数,即 numkeys;
-
KEYS 和 ARGV
必须是大写字母
。
因此这种对应关系类似字符串的格式化
,但是 numkeys 后依次为各参数,而并非完全对应脚本中参数或者value的先后顺序,比如:
127.0.0.1:6379> eval "redis.call('set',KEYS[1],ARGV[1]);redis.call('set',KEYS[2],ARGV[2]);return 'OK';" 2 k2 k3 v2 v3
"OK"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
可以看到 2 后面依次跟着第1、2个参数。
3.3 其他命令
- script load
该命令用于将脚本(不包括后面的numkeys、参数名、参数值
)加载到内存中,但是不执行。
命令格式:script load "脚本内容" 参数... 参数值...
127.0.0.1:6379> script load "redis.call('set',KEYS[1],ARGV[1]);redis.call('set',KEYS[2],ARGV[2]);return 'OK';"
"046ab9327f94abbe991894063370234cb4add8f7"
其中"046ab9327f94abbe991894063370234cb4add8f7"就是校验码。
- evalsha
该命令和eval用法类似,区别在于:- 先要load 脚本到内存中;
- 使用脚本的sha校验码来替代了eval中具体的脚本内容。
127.0.0.1:6379> evalsha 046ab9327f94abbe991894063370234cb4add8f7 2 k2 k3 v2 v3
"OK"
- script exists
该命令用于判断内存中是否存在指定的脚本(使用sha校验码),命令格式为:
script exists 脚本校验码
127.0.0.1:6379> script exists 046ab9327f94abbe991894063370234cb4add8f7
1) (integer) 1
- script flush
该命令用于清除内存中的所有脚本,命令格式为:
script flush
127.0.0.1:6379> script flush
OK
127.0.0.1:6379> script exists 046ab9327f94abbe991894063370234cb4add8f7
1) (integer) 0
可以看到
046ab9327f94abbe991894063370234cb4add8f7 这个脚本缓存已经被清除了。
- script kill
该命令用于kill 掉正在运行的脚本,命令格式为:
script kill
127.0.0.1:6379> script kill
(error) NOTBUSY No scripts in execution right now.
3.2.2 终端 terminal 中使用
命令格式:redis-cli --eval lua_file 参数... 参数值...
redis-cli --eval lua_file key1 key2 , arg1 arg2 arg3
# 如果redis 设置了密码,则需要在redis-cli 后面添加参数 -a root,其中root是设置的密码
笔者水平有限,如有错误,敬请指正!