Lua语法入门

一、前言:为什么 Redis 开发者要学 Lua?

你是否遇到过这些场景?

  • ❌ 需要原子性地执行"判断 + 修改"操作(如限流、扣库存)
  • ❌ 多条 Redis 命令无法保证事务性
  • ❌ 网络往返多次,性能低下

Redis 内置 Lua 脚本引擎 ,让你用一段脚本 完成复杂逻辑,并保证原子执行

而 Lua 本身------轻量、简洁、易学,10 分钟就能上手基础语法。

本文将带你快速掌握 Lua 核心语法,并演示如何在 Redis 中使用!


二、Lua 是什么?

  • 轻量级脚本语言:整个解释器仅几百 KB
  • 嵌入式设计 :被广泛用于游戏(如魔兽世界)、Nginx(OpenResty)、Redis
  • 语法极简:没有类、没有异常、变量无需声明类型

💡 对 Redis 开发者而言 :Lua = 实现原子复合操作的利器!


三、Lua 基础语法速览(对比 Python/JS)

3.1 注释

Lua 复制代码
-- 单行注释
--[[
多行注释
--]]

3.2 变量与数据类型

Lua 只有 8 种基本类型,最常用的是:

  • nil(空值)
  • boolean(true/false)
  • number(整数和浮点统一)
  • string(字符串)
  • table(万能结构:数组 + 字典)
Lua 复制代码
-- 变量无需声明,直接赋值
name = "Alice"        -- string
age = 25              -- number
is_ok = true          -- boolean
nothing = nil         -- nil

-- table:既是数组又是 map
arr = {10, 20, 30}                -- 数组(索引从 1 开始!)
user = {name="Bob", age=30}       -- 字典

⚠️ 重要 :Lua 数组索引从 1 开始,不是 0!

3.3 控制结构

if 判断
Lua 复制代码
if age >= 18 then
    print("成年人")
elseif age > 0 then
    print("未成年人")
else
    print("无效年龄")
end
for 循环
Lua 复制代码
-- 数值 for(推荐)
for i = 1, 5 do
    print(i)  -- 输出 1,2,3,4,5
end

-- 泛型 for(遍历 table)
for key, value in pairs(user) do
    print(key, value)
end
while 循环
Lua 复制代码
local i = 1
while i <= 3 do
    print(i)
    i = i + 1
end

3.4 函数

Lua 复制代码
-- 定义函数
function add(a, b)
    return a + b
end

-- 调用
result = add(3, 5)

-- 函数可作为参数(高阶函数)

3.5 局部变量(强烈推荐!)

Lua 复制代码
local x = 10  -- 局部变量,作用域仅当前 block
global_y = 20 -- 全局变量(不推荐)

最佳实践 :始终用 local 声明变量,避免污染全局环境!


四、Redis 中的 Lua 脚本:核心规则

在 Redis 中执行 Lua 脚本,使用 EVAL 命令:

bash 复制代码
EVAL "脚本内容" key个数 [key ...] [arg ...]

4.1 两个关键全局变量

  • KEYS:数组,存放传入的 key(从 1 开始索引
  • ARGV:数组,存放传入的参数
bash 复制代码
-- 示例:获取第一个 key 的值,并与第一个参数比较
local value = redis.call('GET', KEYS[1])
if tonumber(value) > tonumber(ARGV[1]) then
    return "OK"
else
    return "LOW"
end

4.2 调用 Redis 命令

  • redis.call():执行命令,出错时抛出 Lua 错误
  • redis.pcall():执行命令,出错时返回错误对象(不中断)
bash 复制代码
-- 原子扣减库存
local stock = redis.call('GET', KEYS[1])
if tonumber(stock) >= tonumber(ARGV[1]) then
    redis.call('DECRBY', KEYS[1], ARGV[1])
    return 1  -- 成功
else
    return 0  -- 库存不足
end

五、实战案例:实现分布式限流

使用令牌桶算法,每秒最多 10 次请求。

Lua 脚本(rate_limit.lua)

bash 复制代码
local key = KEYS[1]          -- 限流 key,如 user:1001:rate
local max_count = tonumber(ARGV[1])  -- 最大令牌数(10)
local window = tonumber(ARGV[2])     -- 时间窗口(1秒)

-- 获取当前令牌数和最后更新时间
local current_count = redis.call('HGET', key, 'count')
local last_time = redis.call('HGET', key, 'time')

local now = tonumber(redis.call('TIME')[1])  -- 当前时间戳(秒)

if not last_time then
    -- 第一次访问
    redis.call('HMSET', key, 'count', max_count - 1, 'time', now)
    redis.call('EXPIRE', key, window)
    return 1
else
    local count = tonumber(current_count)
    local elapsed = now - tonumber(last_time)

    if elapsed >= window then
        -- 窗口已过,重置令牌
        count = max_count
    end

    if count > 0 then
        redis.call('HMSET', key, 'count', count - 1, 'time', now)
        redis.call('EXPIRE', key, window)
        return 1  -- 允许
    else
        return 0  -- 拒绝
    end
end

在 Java 中调用(Spring Boot + RedisTemplate)

java 复制代码
String script = Files.readString(Paths.get("rate_limit.lua"));
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);

Long result = redisTemplate.execute(
    redisScript,
    Collections.singletonList("user:1001:rate"),
    "10", "1"
);

if (result == 1) {
    // 请求通过
} else {
    // 触发限流
}

优势 :整个逻辑原子执行,无并发问题!


六、Lua 在 Redis 中的限制

为了保证安全和性能,Redis 对 Lua 脚本做了严格限制:

  • ❌ 不能访问系统时间(只能用 redis.call('TIME')
  • ❌ 不能有随机数(除非用 math.randomseed 固定种子)
  • ❌ 不能有 I/O 操作
  • ❌ 脚本必须是纯函数(相同输入 → 相同输出)

⚠️ 注意 :脚本执行期间,Redis 是单线程阻塞的!务必保证脚本轻量(< 10ms)。


七、调试技巧

7.1 在 Redis CLI 中测试

bash 复制代码
redis-cli EVAL "
local val = redis.call('GET', KEYS[1])
return val or 'NOT_FOUND'
" 1 mykey

7.2 使用 redis.log() 打印日志(仅调试)

bash 复制代码
redis.log(redis.LOG_WARNING, "Debug: count=" .. count)

日志会输出到 Redis 服务端日志文件中。


八、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
爱敲代码的菜菜15 小时前
【测试】自动化测试
css·selenium·测试工具·junit·自动化·xpath
liulilittle18 小时前
范围随机算法实现
开发语言·c++·算法·lua·c·js
柒.梧.18 小时前
Redis通用命令+五大核心数据结构
前端·bootstrap·html
6+h19 小时前
【Redis】底层原理解析(SDS / 跳表 / IO多路复用 / 单线程模型)
数据库·redis·bootstrap
红黑色的圣西罗1 天前
Lua和C#交互探究记录
c#·lua·交互
分享牛2 天前
Operaton入门到精通23-Operaton 2.0 原生支持 JUnit 6 核心指南
数据库·junit
@大迁世界2 天前
6 款轻量级 CLI 工具,取代了我臃肿的开发软件
开发语言·lua
无籽西瓜a2 天前
Docker 环境下 Redis Lua 脚本部署与执行
redis·docker·lua
想做后端的前端3 天前
Lua的元表和元方法
开发语言·junit·lua