一、前言:为什么条件和函数是 Lua 脚本的核心?
在 Redis 中,你经常需要实现这样的逻辑:
- "如果库存足够,就扣减;否则返回失败"
- "对多个 key 执行相同操作,避免重复代码"
条件控制(if)决定逻辑分支,函数(function)实现代码复用 ------它们是构建健壮、可维护 Lua 脚本的两大支柱!
本文将带你深入掌握 Lua 的 if 语句、布尔逻辑、函数定义与调用,并结合 Redis 实战演示。
二、条件控制:if 语句全解析
Lua 的条件判断非常简洁,但有两个关键细节必须注意!
2.1 基本语法
Lua
if condition1 then
-- 执行块1
elseif condition2 then
-- 执行块2
else
-- 执行块3
end
2.2 关键规则 1:只有 nil 和 false 是"假"
Lua
-- 以下值在 if 中都为"真"!
if 0 then print("0 是真") end --> 输出
if "" then print('空字符串是真') end --> 输出
if {} then print("空表是真") end --> 输出
-- 只有这两个是"假"
if nil then else print("nil 是假") end
if false then else print("false 是假") end
⚠️ 常见坑 :不要写
if count == nil or count == 0,直接if not count即可(但注意 0 是真!)
2.3 关键规则 2:关系运算符
| 运算符 | 含义 |
|---|---|
== |
相等 |
~= |
不等(不是 !=) |
<, >, <=, >= |
比较大小 |
Lua
local a = 10
local b = "10"
print(a == b) --> false(类型不同)
print(a == tonumber(b)) --> true
✅ 建议 :比较前统一类型(如用
tonumber()转数字)
三、实战:Redis 脚本中的条件判断
场景:安全扣减库存(防止超卖)
Lua
-- safe_decr_stock.lua
local stock_key = KEYS[1]
local decr_amount = tonumber(ARGV[1])
-- 获取当前库存(字符串转数字)
local current = redis.call('GET', stock_key)
if not current then
-- 库存未初始化
return 0
end
local stock = tonumber(current)
if stock >= decr_amount then
redis.call('DECRBY', stock_key, decr_amount)
return 1 -- 成功
else
return 0 -- 库存不足
end
✅ 优势 :整个判断+修改过程原子执行,无并发问题!
四、函数(Function):代码复用的关键
4.1 定义函数
Lua
-- 方式1:标准定义
function add(a, b)
return a + b
end
-- 方式2:变量赋值(等价)
add = function(a, b)
return a + b
end
4.2 调用函数
Lua
local result = add(3, 5)
print(result) --> 8
4.3 函数特性
-
多返回值 :
Luafunction get_name_age() return "Alice", 25 end local name, age = get_name_age() -
可变参数 :
Luafunction sum(...) local args = {...} local total = 0 for i = 1, #args do total = total + args[i] end return total end print(sum(1, 2, 3)) --> 6
💡 提示 :Redis 脚本中不推荐使用可变参数,因 ARGV 已是 table。
五、Redis 脚本中的函数实践
场景:封装"获取并转数字"逻辑
Lua
-- utils.lua (嵌入到主脚本中)
local function get_number(key)
local val = redis.call('GET', key)
if not val then return 0 end
return tonumber(val)
end
-- 主逻辑
local stock = get_number(KEYS[1])
local quota = get_number(KEYS[2])
if stock > 0 and quota > 0 then
redis.call('DECR', KEYS[1])
redis.call('DECR', KEYS[2])
return "OK"
else
return "INSUFFICIENT"
end
✅ 好处:
- 避免重复代码
- 逻辑更清晰
- 易于测试和维护
六、高级技巧:函数作为参数(高阶函数)
Lua 支持函数式编程风格:
Lua
-- 定义一个通用遍历处理器
local function process_keys(key_list, handler)
for i = 1, #key_list do
handler(key_list[i])
end
end
-- 使用
process_keys(KEYS, function(key)
redis.call('EXPIRE', key, 3600)
end)
🔧 适用场景:批量设置过期时间、统一格式化等。
七、避坑指南:Redis 脚本中的函数限制
❌ 不能定义全局函数(除非必要)
Lua
-- 危险!污染全局环境
function my_func() ... end
-- 安全:用 local
local function my_func() ... end
❌ 不能递归过深(Redis 有栈深度限制)
- 避免深度递归,改用循环
❌ 函数内不能有随机行为
Lua
-- 错误:math.random() 在 Redis 中禁止(除非固定种子)
local function random_val()
return math.random(1, 10) -- 可能导致主从不一致!
end
✅ 替代方案:随机数由客户端生成,通过 ARGV 传入。
八、小结:核心要点速记
| 主题 | 关键规则 |
|---|---|
| if 判断 | 只有 nil 和 false 为假;0、""、{} 都是真 |
| 不等于 | 用 ~=,不是 != |
| 函数定义 | local function name(...) ... end |
| Redis 安全 | 函数必须是纯函数(无副作用、无随机) |
| 代码复用 | 将通用逻辑(如类型转换)封装成 local 函数 |
九、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!