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 脚本的入门三板斧

相关推荐
啦啦啦_99991 天前
Redis-0-业务逻辑
数据库·redis·缓存
自不量力的A同学1 天前
Redisson 4.2.0 发布,官方推荐的 Redis 客户端
数据库·redis·缓存
fengxin_rou1 天前
[Redis从零到精通|第四篇]:缓存穿透、雪崩、击穿
java·redis·缓存·mybatis·idea·多线程
是阿楷啊1 天前
Java大厂面试场景:音视频场景中的Spring Boot与微服务实战
spring boot·redis·spring cloud·微服务·grafana·prometheus·java面试
笨蛋不要掉眼泪1 天前
Redis哨兵机制全解析:原理、配置与实战故障转移演示
java·数据库·redis·缓存·bootstrap
ALex_zry1 天前
Redis Cluster 分布式缓存架构设计与实践
redis·分布式·缓存
乔江seven2 天前
【Flask 进阶】3 从同步到异步:基于 Redis 任务队列解决 API 高并发与长耗时任务阻塞
redis·python·flask
这周也會开心2 天前
Redis与MySQL回写中的数据类型存储设计
数据库·redis·mysql
shuair2 天前
redis缓存预热、缓存击穿、缓存穿透、缓存雪崩
redis·spring·缓存
AI_56782 天前
Postman接口测试提速技巧:批量请求+智能断言实践
测试工具·lua·postman