Redis Lua 脚本核心语法详解:KEYS[1]、ARGV[1]、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 key
  • ARGV[1]:Lua 脚本中第一个普通参数
  • tonumber:把 Redis / ARGV 的字符串转换成数字

这三个概念,是 Redis Lua 脚本的入门三板斧

相关推荐
secondyoung2 小时前
Pandoc转换Word文档:使用Lua过滤器统一调整Pandoc文档中的图片和表格格式
经验分享·junit·word·lua·markdown·pandoc·mermaid
虹科网络安全10 小时前
艾体宝洞察 | Redis vs ElastiCache:哪个更具成本效益?
数据库·redis·缓存
a努力。14 小时前
美团Java面试被问:Redis集群模式的工作原理
java·redis·后端·面试
关于不上作者榜就原神启动那件事16 小时前
Spring Data Redis 中的 opsFor 方法详解
java·redis·spring
木风小助理16 小时前
分布式系统统一限流:基于Redis与Lua的跨实例流量管控方案
junit
一岁天才饺子17 小时前
Redis漏洞复现
redis·web安全·网络安全
Boilermaker199218 小时前
[Redis] 分布式缓存与分布式锁
redis·分布式·缓存
虹科网络安全18 小时前
艾体宝产品 | 隆重推出 Haink:Redis 的应用型 AI 智能体
数据库·人工智能·redis
镜花水月linyi19 小时前
MySQL与Redis缓存一致性方案
redis·后端·mysql