redis lua脚本中redis.call("get", "missing:key")返回false而非nil,因redis协议将nil bulk reply映射为lua布尔值false;正确判断应使用if not val,而非if val == nil。

Redis Lua脚本里 redis.call("GET", "missing:key") 返回的是 false,不是 nil
这是最常踩的坑:很多人写 if val == nil then 判断缺失值,结果永远进不去分支。因为 Redis 协议规定,Nil bulk reply(比如 key 不存在)在 Lua 环境中被映射为 Lua 的 false 布尔值,而非 nil。
验证方式很简单:
EVAL "local a = redis.call('GET', KEYS[1]); return type(a)" 1 missing:key
返回 "boolean",不是 "nil"。
redis.call("GET", "missing:key") → falseredis.call("HGET", "h", "missing_field") → false-
redis.call("HMGET", "h", "a", "b") → {false, false}(每个缺失字段都是false) - 直接用
if not val then是安全的——Lua 中false和nil都是 falsy
用 return false 表达业务失败?别这么做
你写 return false,客户端收到的和 return nil 完全一样(都是 None 或 null),根本无法区分“键不存在”和“业务逻辑明确拒绝”。
后果是:客户端只能靠上下文猜,或者加额外 round-trip 查状态,破坏原子性。
- ✅ 正确做法:用 table 包装语义,例如
return {success = false, code = "NOT_FOUND"} - ✅ 更推荐:Redis 7.0+ 可用
redis.call("debug", "error", "user: not found")主动报错,客户端能直接捕获异常 - ❌ 错误写法:
if not user_data then return false end—— 这会让调用方无法判断是数据缺失还是策略拦截
OpenResty 的 lua-resty-redis 返回 ngx.null,和 Lua 的 nil 不是一回事
这是另一个独立但高频的混淆点:OpenResty 自己的 Redis 客户端(非 redis.call)在 key 不存在时返回的是 ngx.null,一种 userdata 类型,既不等于 nil,也不等于 false。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
典型错误:
local res, err = red:get("token")
if res == nil then ... -- 永远不成立
if res == false then ... -- 也不成立
正确判断必须显式比对:
if res == ngx.null or res == nil then-
if type(res) == "userdata" and tostring(res) == "null" then(不推荐,依赖 tostring 实现) - 更稳妥:先检查
err是否存在,再判断res类型
HMGET / HMSET 等批量命令返回 table,但元素是 false,不是 nil
比如 redis.call("HMGET", "user:1", "name", "age", "email") 返回 {'Alice', false, false},而不是 {'Alice', nil, nil}。table 本身不为空,但某些索引值是 false。
所以不能用 next(tbl) == nil 或 #tbl == 0 判断是否全空;也不能用 tbl[2] == nil 判缺失字段。
- ✅ 正确检查第 2 个字段是否存在:
if tbl[2] == false then - ✅ 批量过滤空值:
for i, v in ipairs(tbl) do if v ~= false then ... end - ⚠️ 注意:
table.insert(tbl, nil)会截断数组,Lua table 中nil不是合法数组元素
Redis 的空值语义在不同上下文(redis.call vs OpenResty client)和不同命令(单值 vs 多值)间不一致,靠直觉容易翻车。最稳的路径是:统一用 == false 判断 redis.call 结果,用 == ngx.null 判断 OpenResty 返回,并永远避免裸 return false。










