Redis里到底咋运行Lua脚本,步骤和注意点有哪些?
- 问答
- 2026-01-11 16:07:54
- 1
Redis里运行Lua脚本的步骤和注意点
在Redis里运行Lua脚本,主要是为了实现复杂的、需要多个Redis命令按顺序原子性执行的操作,你可以把它想象成,你把一连串的命令指令写在一个小本子(Lua脚本)上,然后一次性把这个小本子交给Redis服务员,让他从头到尾不间断地执行,中间不会插队处理别人的命令,这保证了操作的原子性。
运行Lua脚本的核心步骤
运行Lua脚本主要有两种方式:一种是使用 EVAL 命令直接执行,另一种是使用 SCRIPT LOAD 和 EVALSHA 组合执行,后者是为了提高效率。
步骤1:使用 EVAL 命令直接执行
这是最直接的方法,你直接把写好的Lua脚本代码作为参数传递给Redis。
-
命令格式:
EVAL "你的Lua脚本代码" numkeys key [key ...] arg [arg ...]
-
参数解释:
"你的Lua脚本代码":这是一个字符串,里面就是你用Lua语言写的脚本。numkeys:用于指定后面跟着的键(key)的数量,这个参数必须传,即使是0。key [key ...]:你的脚本中会操作到的Redis键(key),可以有一个或多个,数量由numkeys指定,这样设计是为了帮助Redis在集群模式下确定脚本应该被发送到哪个节点执行。arg [arg ...]:传递给Lua脚本的附加参数,这些参数在脚本中可以通过全局变量ARGV数组来访问。
-
举个例子: 假设我们想实现一个“条件性设置”的功能:只有当某个键不存在时,才设置它的值。 Lua脚本可能是这样的:
if redis.call('exists', KEYS[1]) == 0 then return redis.call('set', KEYS[1], ARGV[1]) else return nil end在Redis客户端中,你会这样执行:
EVAL "if redis.call('exists', KEYS[1]) == 0 then return redis.call('set', KEYS[1], ARGV[1]) else return nil end" 1 mykey "hello world"1表示后面有1个key。mykeyKEYS[1]。"hello world"ARGV[1]。
步骤2:使用 SCRIPT LOAD 和 EVALSHA 命令(推荐用于生产环境)
如果脚本很长,每次执行都传递完整的脚本字符串会非常耗费网络带宽。EVAL 命令的替代方案是先用 SCRIPT LOAD 命令将脚本“缓存”到Redis服务器上,它会返回一个该脚本的SHA1校验和(可以理解成一个唯一的身份证号),之后,要执行这个脚本时,只需要使用 EVALSHA 命令带上这个身份证号就可以了。

-
具体步骤:
- 缓存脚本:
SCRIPT LOAD "你的Lua脚本代码",Redis会返回一个SHA1字符串,"a3a2e2000c0e8c0a0a0a0a0a0a0a0a0a0a0a0a0a0"。 - 通过SHA值执行:
EVALSHA sha1值 numkeys key [key ...] arg [arg ...],参数格式和EVAL完全一样,只是把脚本代码换成了SHA值。
- 缓存脚本:
-
继续上面的例子:
SCRIPT LOAD "if redis.call('exists', KEYS[1]) == 0 then return redis.call('set', KEYS[1], ARGV[1]) else return nil end"返回:
"a3a2e2000c0e8c0a0a0a0a0a0a0a0a0a0a0a0a0a0"然后执行:EVALSHA a3a2e2000c0e8c0a0a0a0a0a0a0a0a0a0a0a0a0a0 1 mykey "hello again"这种方式效率高很多,是生产环境中的最佳实践。
重要的注意点

-
原子性是优势也是风险:Lua脚本在执行时,整个Redis服务器会阻塞,不会处理任何其他命令。绝对不能在Lua脚本中执行耗时操作,比如长时间的循环、调用外部服务、执行
keys *这种可能很慢的命令,否则会导致整个Redis服务卡住,所有请求超时,这是致命的。 -
脚本的返回值:Lua脚本的最后一行结果,或者你在脚本中通过
return语句返回的值,就是整个脚本执行后返回给客户端的结果,Redis会将Lua的数据类型转换为Redis的协议类型,如果你不需要返回值,比如只是执行一些删除操作,返回nil或者一个简单的数字(如return 1表示成功)都是可以的。 -
如何与Redis交互:在Lua脚本内部,你不能直接使用Redis命令,必须通过两个特定的Lua函数:
redis.call():用于执行Redis命令,如果命令执行出错(比如对字符串执行哈希表命令),这个错误会直接抛出一个Lua异常,导致整个脚本停止执行,并且之前已经执行的命令也不会回滚。redis.pcall():功能和redis.call()类似,关键区别在于错误处理,如果命令出错,redis.pcall()会捕获错误,并以Lua表(table)的形式返回错误信息,而不会抛出异常,这样脚本可以继续执行,给你一个处理错误的机会,大多数情况下,如果你不确定一个命令是否会出错,或者希望自己控制错误逻辑,就用pcall。
-
脚本的复用和缓存:Redis服务器会缓存它执行过的所有脚本(通过SHA1标识),你可以用
SCRIPT EXISTS sha1值来检查一个脚本是否已被缓存,可以用SCRIPT FLUSH清空所有脚本缓存(重启Redis也会清空),在生产环境中,建议在应用启动时,通过SCRIPT LOAD预先加载所有需要的脚本,确保脚本的SHA值可用。 -
调试困难:Redis原生对Lua脚本的调试支持很弱,脚本一旦投入生产环境,如果逻辑复杂,出了问题很难排查,务必在测试环境充分测试你的脚本逻辑,可以将复杂的逻辑拆分成多个简单的脚本,降低复杂度。
-
注意数据类型转换:Lua和Redis的数字类型都是浮点数,但Lua只有一种数字类型,而Redis有整数和浮点数之分,在来回传递数字时需要注意精度问题,通常字符串和表的转换比较直观。
在Redis中使用Lua脚本是一个非常强大的功能,但它是一把双刃剑,核心要领是:利用其原子性优势,规避其阻塞风险,通过EVALSHA提升效率,并在脚本中谨慎处理错误和耗时操作。
本文由盈壮于2026-01-11发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/78781.html
