文章目录
- [一、Redis Lua 脚本的参数模型](#一、Redis Lua 脚本的参数模型)
- [二、KEYS[1] 是什么意思?](#二、KEYS[1] 是什么意思?)
- [三、ARGV[1] 是什么意思?](#三、ARGV[1] 是什么意思?)
- [四、tonumber 是什么意思?为什么一定要用?](#四、tonumber 是什么意思?为什么一定要用?)
- 五、三个概念放在一起的完整示例(库存脚本)
- 六、常见误区总结(你很可能踩过)
- 七、总结
在使用 Redis Lua 脚本时,你几乎一定会看到下面这些写法:
lua
redis.call('get', KEYS[1])
local num = tonumber(ARGV[1])
很多人在第一次接触 Lua + Redis 时都会有疑问:
KEYS[1]是什么?为什么从 1 开始?ARGV[1]又是什么?和KEYS有什么区别?tonumber为什么几乎"必写"?- 这些东西在 Java 里是怎么传进来的?
一、Redis Lua 脚本的参数模型
在 Redis 中,Lua 脚本的参数只有两类:
text
KEYS ------ 存 Redis key
ARGV ------ 存普通参数
Lua 脚本本身 不能直接接收任意参数 ,
Redis 强制你把参数分成这两组。
Redis 执行 Lua 的基本语法
redis
EVAL <lua-script> <numkeys> <key1> <key2> ... <arg1> <arg2> ...
例如:
redis
EVAL "return {KEYS[1], ARGV[1]}" 1 product:stock:1 5
二、KEYS[1] 是什么意思?
1、KEYS 是什么?
KEYS是 Redis 传给 Lua 的"Key 数组"
lua
KEYS = { key1, key2, key3, ... }
2、为什么是 KEYS[1],而不是 KEYS[0]?
因为 Lua 数组下标从 1 开始。
lua
KEYS[1] -- 第一个 key
KEYS[2] -- 第二个 key
👉 这是 Lua 语言的规则,不是 Redis 的设计失误。
3、KEYS[1] 在脚本中的真实含义
lua
redis.call('get', KEYS[1])
等价于:
从 Redis 中获取第一个传入的 key 对应的值
示例:库存 key
redis
EVAL "return redis.call('get', KEYS[1])" 1 product:stock:1
Lua 中:
lua
KEYS[1] == "product:stock:1"
5、为什么 Redis 强制区分 KEYS?
原因只有一个:
安全 + 集群路由
- Redis Cluster 需要提前知道哪些是 key
- 才能把脚本路由到正确的节点
- 如果 key 混在普通参数里,Redis 无法判断
👉 所以:Redis 规定 key 必须放在 KEYS 里
三、ARGV[1] 是什么意思?
ARGV 是什么?
ARGV是 Redis 传给 Lua 的"普通参数数组"
lua
ARGV = { arg1, arg2, arg3, ... }
ARGV 和 KEYS 的核心区别
| 对比项 | KEYS | ARGV |
|---|---|---|
| 是否 Redis key | ✅ 是 | ❌ 否 |
| 是否参与路由 | ✅ 是 | ❌ 否 |
| 用途 | get / set / decr | 数量、阈值、参数 |
示例:扣减库存数量
redis
EVAL "return ARGV[1]" 1 product:stock:1 3
Lua 中:
lua
ARGV[1] == "3"
👉 注意:ARGV 永远是字符串
四、tonumber 是什么意思?为什么一定要用?
tonumber 是 Lua 的类型转换函数
lua
tonumber("10") --> 10
tonumber(nil) --> nil
为什么 ARGV 一定要 tonumber?
因为:
Redis 传给 Lua 的所有参数,本质上都是字符串
例如:
lua
ARGV[1] == "3"
如果你直接做数学运算:
lua
if ARGV[1] <= 0 then -- ❌ 错误
Lua 会直接报错。
正确写法(必须)
lua
local num = tonumber(ARGV[1])
if num <= 0 then
return -1
end
tonumber + redis.call 的经典组合
lua
local stock = tonumber(redis.call('get', KEYS[1]))
含义是:
把 Redis 中存的字符串库存,转换成 Lua 可计算的数字
五、三个概念放在一起的完整示例(库存脚本)
lua
-- KEYS[1] : 库存 key
-- ARGV[1] : 扣减数量
local stock = tonumber(redis.call('get', KEYS[1]))
local num = tonumber(ARGV[1])
-- 库存不存在
if not stock then
return -2
end
-- 库存不足
if stock < num then
return -1
end
redis.call('decrby', KEYS[1], num)
return stock - num
对应的 Redis 调用
redis
EVAL "<lua-script>" 1 product:stock:1 2
对应的 Java 调用(Spring Boot)
java
redisTemplate.execute(
script,
Collections.singletonList("product:stock:1"),
2
);
六、常见误区总结(你很可能踩过)
❌ 误区 1:KEYS 和 ARGV 随便用
👉 key 一定放 KEYS
❌ 误区 2:不写 tonumber
lua
if ARGV[1] > 0 then -- ❌
👉 字符串不能直接比较
❌ 误区 3:用 KEYS 传普通参数
lua
KEYS[1] = "3" -- ❌
👉 会破坏 Redis Cluster 路由
七、总结
KEYS[1]:Lua 脚本中第一个 Redis keyARGV[1]:Lua 脚本中第一个普通参数tonumber:把 Redis / ARGV 的字符串转换成数字
这三个概念,是 Redis Lua 脚本的入门三板斧。