Redis 入门(三):订阅/发布、事务、脚本

一、Redis 消息通信模式 -- 发布/订阅

Publish/Subscribe 是 Redis 的消息通信模式,某些 redis client 可以去订阅某些channel,然后可以启用某些 redis client 向某些chenel发布message,然后message就会被推送给那些申请订阅的客户端,这个过程就像是预定/上门配送光明牛奶的过程。

客户端订阅某channel

发布推送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是设置的密码


笔者水平有限,如有错误,敬请指正!

你可能感兴趣的:(Redis 入门(三):订阅/发布、事务、脚本)