Lua 是一种轻量级脚本语言 Redis 在内部嵌入了 Lua 解释器(用 C 实现)
👉 也就是说:
Redis(C写) + Lua解释器(C写) → 支持执行Lua脚本
为了讲清楚 Lua 脚本,小编帮你把 Lua 在 Redis 里的作用讲清楚,从"为什么存在 → 解决什么问题 → 怎么用"这条路线开始。
🧠 一、先纠正一个关键理解
由于 Redis 是 C 语言编写的,那 Lua 也是 语言封装了一层的工具吗?
✔ 部分对,但不完整
✅ 更准确的说法是:
Lua 是一种轻量级脚本语言
Redis 在内部嵌入了 Lua 解释器(用 C 实现)
【真实情况】Redis(C写) + Lua解释器(C写) → 支持执行Lua脚本
🚀 二、Redis 为什么要支持 Lua?
👉 核心一句话:
让多个命令"原子执行 + 一次完成"
❌ 没有 Lua 的问题
比如你做一个业务:
库存判断 + 扣库存
普通写法:
if (GET stock > 0) {
DECR stock
}
🚨 问题来了(并发下):
线程A: GET → 1
线程B: GET → 1
线程A: DECR → 0
线程B: DECR → -1 ❌
👉 超卖问题
🔥 三、Lua 的核心价值
✅ 1️⃣ 原子性(最重要🔥)
Lua 脚本在 Redis 中执行 = 单线程 + 原子执行
👉 等价于:
整个脚本 = 一个命令
✅ 2️⃣ 减少网络往返(性能)
多次 Redis 请求 → 变成一次 EVAL
✅ 3️⃣ 业务逻辑下沉到 Redis
逻辑在 Redis 内执行,而不是客户端拼
🧩 四、Redis Lua 是怎么工作的?
👉 执行方式
EVAL "lua脚本" key数量 key1 key2 ... arg1 arg2 ...
📌 示例(库存扣减)
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock > 0 then
return redis.call('DECR', KEYS[1])
else
return -1
end
👉Lua 执行
EVAL "
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock > 0 then
return redis.call('DECR', KEYS[1])
else
return -1
end
" 1 stock
🔥 五、Lua vs 事务(MULTI/EXEC)
👉 很多人误区:Lua = 事务?
❌ 不一样
对比一下:
|------|----------|--------------|
| 特性 | Lua | Redis事务 |
| 原子性 | ✅ 强原子 | ⚠️ 弱(中间可能失败) |
| 支持逻辑 | ✅ if/for | ❌ 不支持 |
| 回滚 | ❌ 不支持 | ❌ 也不支持 |
| 并发安全 | ✅ | ⚠️ 依赖WATCH |
🧠 本质区别
Lua = "可编程原子操作"
事务 = "命令打包执行"
🧪 六、再给你一个真实业务例子(限流)
👉 需求:
1秒最多访问5次
Lua 实现:
local count = redis.call('INCR', KEYS[1])
if count == 1 then
redis.call('EXPIRE', KEYS[1], 1)
end
if count > 5 then
return 0
else
return 1
end
👉 调用:
EVAL "脚本" 1 user:limit
🧠 这段做了什么?
INCR + EXPIRE + 判断 → 原子执行
🧨 七、Lua 在 Redis 中的本质定位
👉 一句话总结
Lua = Redis 内部的"小型业务执行引擎"
👉 再换一种更工程化说法
它让 Redis 从:
👉 KV存储
变成:
👉 支持业务逻辑执行的"内存计算节点"
🚀 八、Lua 语法
学 Redis Lua,不是为了学 Lua 本身,而是:
👉 用 Lua 在 Redis 里写"原子业务逻辑"
所以学习重点是:
Redis思维 > Lua语法
Java 调 Redis:
多次网络请求 + 客户端控制逻辑
Lua in Redis:
一次请求 + Redis内部执行逻辑 + 原子保证
🥇 第一阶段:Lua 最小语法(够用就行)
👉 目标:能看懂 & 写简单脚本
- Lua 基础语法(变量、if、for)
redis.call- KEYS / ARGV
1️⃣ 变量
local a = 10
local b = "hello"
2️⃣ if 判断
local x = 5
if x > 3 then
return 1
else
return 0
end
3️⃣ 基本类型
number(数字)
string(字符串)
boolean(布尔)
nil(空)
4️⃣ 函数(了解即可)
local function add(a, b)
return a + b
end
👉 在 Redis Lua 中很少写复杂函数
🥈 第二阶段:Redis Lua 核心(重点🔥)
👉 这一阶段才是真正关键
- 原子操作设计(库存 / 限流 / 幂等)
- 脚本复用(EVALSHA)
1️⃣ redis.call(最重要)
redis.call("GET", "key")
redis.call("SET", "key", "value")
📌 示例
local value = redis.call("GET", "name")
return value
2️⃣ KEYS 和 ARGV(必须掌握)
👉 KEYS(Redis key)
KEYS[1]
KEYS[2]
👉 ARGV(参数)
ARGV[1]
ARGV[2]
📌 示例
local key = KEYS[1]
local value = ARGV[1]
redis.call("SET", key, value)
return "OK"
👉 调用方式
EVAL "脚本" 1 mykey myvalue
解释:
1 = key数量
mykey → KEYS[1]
myvalue → ARGV[1]
🥉 第三阶段:核心能力(真正有价值🔥)
1️⃣ 原子操作(核心)
👉 Redis Lua 最大价值:
多个操作 → 一次执行(原子)
📌 示例:安全扣库存
local stock = tonumber(redis.call("GET", KEYS[1]))
if stock <= 0 then
return -1
end
redis.call("DECR", KEYS[1])
return 1
🚨 重点
整个脚本 = 原子操作(不会被打断)
2️⃣ 限流(经典面试题🔥)
local count = redis.call("INCR", KEYS[1])
if count == 1 then
redis.call("EXPIRE", KEYS[1], 1)
end
if count > 5 then
return 0
else
return 1
end
3️⃣ 幂等控制(防重复)
if redis.call("EXISTS", KEYS[1]) == 1 then
return 0
else
redis.call("SET", KEYS[1], 1)
return 1
end
🏅 第四阶段:进阶(拉开差距🔥)
1️⃣ EVALSHA(脚本缓存)
第一次 → EVAL
之后 → EVALSHA(更快)
2️⃣ 性能 & 风险(很重要⚠️)
❗ Redis Lua 是单线程执行
脚本执行期间 → Redis 被阻塞
🚨 禁忌
❌ 死循环
❌ 大量计算
❌ 长时间运行
3️⃣ 设计原则
✔ 脚本要短
✔ 逻辑要简单
✔ 只做必要操作
🧪 第五阶段:从 0 到 1 的练习(强烈建议你做)
我给你一个入门练习顺序👇
🥇 练习 1:读写
SET + GET
🥈 练习 2:计数器
INCR + 返回结果
🥉 练习 3:库存扣减
GET + 判断 + DECR
🏅 练习 4:限流(重点)
INCR + EXPIRE + 判断
🔥 最后一句话总结
Lua = 让 Redis 具备"执行业务逻辑能力"的武器