一、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脚本虽然好用,但是如果使用姿势不正确,后果也是十分严重的。
领取专属 10元无门槛券
私享最新 技术干货