分布式缓存Redis之Script脚本

一、简介
  Redis 脚本使用单个Lua 解释器来执行脚本,并且Redis 也保证脚本会以原子性(atomic)的方式执行:当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。这和使用 MULTI / EXEC 包围的事务很类似。在其他别的客户端看来,脚本的效果(effect)要么是不可见的(not visible),要么就是已完成的(already completed)。 Reids 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。

  使用lua脚本的好处:

1:减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延;
2:原子操作。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
3:复用。客户端发送的脚本会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。

  现在Lua脚本用在很多游戏上,主要是Lua脚本可以嵌入到其他程序中运行,游戏升级的时候,可以直接升级脚本,而不用重新安装游戏。比如游戏的很多关卡,只需要增加lua脚本,在游戏中嵌入Lua解释器,游戏团队线上更新Lua脚本,然后游戏自动下载最新的游戏关卡。例如之前很多的游戏《愤怒的小鸟》就是用Lua语言实现的关卡。

二、常用命令
1)Eval 命令:使用 Lua 解释器执行脚本
  
语法:

redis 127.0.0.1:6379> EVAL script numkeys key [key ...] arg [arg ...]

参数说明:
script: 参数是一段 Lua 5.1 脚本程序。脚本不必(也不应该)定义为一个 Lua 函数。
numkeys: 用于指定键名参数的个数。当脚本不需要任何参数时,也不能省略这个参数(设为0)
key [key …]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)
arg [arg …]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
  比如:

è¿éåå¾çæè¿°

  其中 “return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}” 是被求值的 Lua 脚本,数字 2 指定了键名参数的数量, key1 和 key2 是键名参数,分别使用 KEYS[1] 和 KEYS[2] 访问,而最后的 first 和 second 则是附加参数,可以通过 ARGV[1] 和 ARGV[2] 访问它们。

2)Evalsha 命令:根据给定的 sha1 校验码,执行缓存在服务器中的脚本

redis 127.0.0.1:6379> EVALSHA sha1 numkeys key [key ...] arg [arg ...]

参数说明:
sha1: 脚本的校验码。
实例:

è¿éåå¾çæè¿°

注意:EVAL与EVALSHA区别:

EVAL命令会在每次执行脚本的时候都发送一次脚本主体,它不会每次都重新编译,但是很多时候它付出了无必要的带宽来传递主体;而EVALSHA命令,它的作用与EVAL相同,但是它解决上面EVAL的带宽消耗,也就是它接受的第一个参数不是脚本而是脚本的SHA1校验和(SUM)。
如果服务器还存在给定的 SHA1 校验和所指定的脚本,那么就执行这个脚本;如果服务器不存在给定的 SHA1 校验和所指定的脚本,那么它返回一个特殊的错误:提醒用户使用 EVAL 代替 EVALSHA。
EVAL 和 EVALSHA 可以在 O(1) 复杂度内找到要被执行的脚本,其余的复杂度取决于执行的脚本本身。
客户端库的底层实现可以一直乐观地使用 EVALSHA 来代替 EVAL ,并期望着要使用的脚本已经保存在服务器上了,只有当 NOSCRIPT 错误发生时,才使用 EVAL 命令重新发送脚本,这样就可以最大限度地节省带宽。

这也说明了执行 EVAL 命令时,使用正确的格式来传递键名参数和附加参数的重要性:因为如果将参数硬写在脚本中,那么每次当参数改变的时候,都要重新发送脚本,即使脚本的主体并没有改变,相反,通过使用正确的格式来传递键名参数和附加参数,就可以在脚本主体不变的情况下,直接使用 EVALSHA 命令对脚本进行复用,免去了无谓的带宽消耗。

3)Script Exists 命令:根据脚本的校验码,校验指定的脚本是否已经被保存在缓存当中

语法:

redis 127.0.0.1:6379> SCRIPT EXISTS script [script ...]


返回值:一个列表,包含 0 和 1 ,前者表示脚本不存在于缓存,后者表示脚本已经在缓存里面了。

实例:

è¿éåå¾çæè¿°

4)Script Flush 命令:用于清除所有 Lua 脚本缓存
 

redis 127.0.0.1:6379> SCRIPT FLUSH

5)Script kill 命令:用于杀死当前正在运行的 Lua 脚本,当且仅当这个脚本没有执行过任何写操作时,这个命令才生效。这个命令主要用于终止运行时间过长的脚本,比如一个因为 BUG 而发生无限循环的脚本。

SCRIPT KILL 执行之后,当前正在运行的脚本会被杀死,执行这个脚本的客户端会从 EVAL 命令的阻塞当中退出,并收到一个错误作为返回值。

语法:
 

redis 127.0.0.1:6379> SCRIPT KILL

6)Script Load 命令:用于将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本

语法:

redis 127.0.0.1:6379> SCRIPT LOAD script

返回值:给定脚本的 SHA1 校验和。

注意:EVAL 命令也会将脚本添加到脚本缓存中,但是它会立即对输入的脚本进行求值。

如果给定的脚本已经在缓存里面了,那么不执行任何操作。在脚本被加入到缓存之后,通过 EVALSHA 命令,可以使用脚本的 SHA1 校验和来调用这个脚本。脚本可以在缓存中保留无限长的时间,直到执行 SCRIPT FLUSH 为止。

实例:


猜你喜欢

转载自blog.csdn.net/luzhensmart/article/details/86489684