Redis Lua的使用

一、Lua 的介绍

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入到应用程序中,从而为应用程序提供灵活的扩展和定制功能。

Lua 的特性

轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。

可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。

其它特性:

支持面向过程(procedure-oriented)编程和函数式编程(functional programming);

自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;

语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;

通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。

Lua 虽然简单小巧,但是功能十分强大,很多大公司和中间件都用到了Lua,比如说:

暴雪公司将Lua引入到“魔兽世界”中

Rovio公司将Lua引入到“愤怒的小鸟”中

Nginx将Lua作为扩展语言

Redis支持执行Lua脚本

这里就不对 Lua 做过多的介绍了,想全面学习Lua的可以去Lua官网进行学习。

二、Redis中使用Lua

Redis支持执行Lua脚本,Lua脚本提供了Redis命令更多的灵活性。

1.Redis中执行Lua脚本

在Redis中执行Lua脚本有两种方法:eval和evalsha。

eval 命令

eval 命令的语法规则是:

比如说:

上面这条命令中Lua脚本内容是'return "I " .. KEYS[1] .. ARGV[1]',其中 .. 表示连接两个字符串,key个数是 1 ,KEYS[1] = am,ARGV[1] = lebron。

evalsha 命令

有时候Lua脚本内容太长放在eval命令中不合适,并且每次执行eval命令中都要带上Lua脚本,会增大请求的网络开销。所以Redis提供了 evalsha 命令来解决这个的问题。

我们可以将需要执行的Lua脚本加载到Redis内存中,Redis会返回这个Lua脚本的SHA1校验和,之后每次执行Lua脚本的时候只需要使用 evalsha 命令搭配上这个SHA1校验和就行了。

evalsha 命令的参数传递格式和 eval 命令一样,两个命令的唯一区别就在于是使用SHA校验和还是原生Lua脚本。

将Lua脚本加载到Redis内存之后,脚本功能就能得到复用,从而降低网络开销。

可以使用 script load 命令将脚本内容加载到Redis内存,并且返回SHA1校验和

也可以将脚本内容写到文件中,通过加载Lua脚本文件来将Lua脚本加载到Redis内存

Lua脚本加载到Redis内存中之后就可以使用 evalsha 命令去执行Lua脚本了

3、Lua脚本中使用Redis命令

上面案例中的Lua脚本只是最简单的字符串拼接和返回,没有涉及到Redis命令。

在Lua脚本中可以使用 redis.call 来调用Redis的命令:

在Lua脚本中也可以使用 redis.pcall 来调用Redis的命令,两个命令的区别在于如果 redis.call 命令执行失败,那么Lua脚本结束并且返回错误;如果 redis.pcall 命令执行失败,会忽略错误继续执行脚本。

使用分布式锁的时候一般需要使用到 setnx 命令来抢占锁,然后需要使用 expire 命令对锁设置一个有效期,防止发生异常导致锁永远不被释放。但是Redis没有提供一个命令来同时实现这两个功能,我们可以使用Lua脚本来帮我们做到这个事情。

下面开始使用Lua脚本来帮我们实现这个原子操作:

创建Lua脚本文件

创建 setnxex.lua 文件,写入Lua脚本:

将Lua脚本文件写到Redis内存

得到Lua脚本文件的SHA1校验和。

使用 evalsha 命令抢占分布式锁

返回 1 表示抢占分布式锁成功

返回 0 表示抢占分布式锁失败

4、管理Redis内存中的Lua脚本

Redis提供了 4 个命令实现对Lua脚本的管理。

script load

这个命令用于将Lua脚本加载到Redis内存,前面已经介绍过了。

scripts exists

这个命令用于校验 sha1 [sha1 …] 被加载到Redis内存的个数。

只传递一个参数就可以校验这个脚本有没有被加载到Redis内存。

script flush

这个命令用于清除Redis内存中的所有Lua脚本。

script kill

这个命令用于杀死正在执行的Lua脚本,如果我们的Lua脚本执行比较耗时或者Lua脚本有问题导致进入了死循环,可以调用这个命令来杀死正在进行的Lua脚本命令。

三、总结

优点

Lua脚本在Redis中是原子执行的,执行过程不会受其他客户端命令影响

Lua脚本可以帮助我们定制化符合自己业务的命令,并且可以常驻内存以达到复用的效果

可以将一些批处理任务写到Lua脚本中,减少网络开销

缺点

Redis中提供了一个 lua-time-limit 的配置,默认 5 秒,它是Redis Lua脚本的“超时时间”。

一旦Lua脚本执行时间超过这个值之后,Redis会停止对其他客户端的正常服务。其他客户端在执行命令的时候会收到下面的错误:

此时Redis已经阻塞,无法对外正常提供服务,可以使用 script kill 命令停止Lua脚本命令或者直接使用 shutdown save 命令停止Redis服务。

一般遇到这种情况我们肯定倾向于使用 script kill 命令命令来停止Lua脚本而不是直接停止Redis服务。但是如果Lua脚本中已经执行过写操作的情况下,执行 script kill 命令会报错并且不会停止Lua脚本的执行,只能够停止Redis服务。

可见Redis Lua脚本虽然好用,但是如果使用姿势不正确,后果也是十分严重的。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180904G0U2T500?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券