能。redis lua脚本在单线程中串行执行,期间无其他命令插入,保证执行不中断;但已成功执行的redis.call()命令不可回滚,仅具“执行原子性”,非acid事务原子性。

能。Redis Lua脚本天然具备原子性——整个脚本在单线程中串行执行,期间不会有其他客户端命令插入,但要注意:它不回滚已执行的命令,只保证“不被中断”,不是 ACID 意义上的原子性。
为什么 EVAL/EVALSHA 能保证执行不被打断
Redis 主线程是单事件循环模型。当你调用 EVAL 或 EVALSHA 时,整个 Lua 脚本会在主线程上同步阻塞运行,直到结束。这意味着:
- 脚本内所有
redis.call()都直接进入内部执行队列,跳过网络解析和排队环节 - 其他客户端发来的任何命令(包括
GET、SET、甚至SCRIPT KILL)都会被挂起等待 - 集群模式下,Redis 会提前校验
KEYS所在 slot 是否一致,不一致直接报CROSSSLOT错误,不会执行
KEYS 和 ARGV 必须严格区分传参方式
传 key 名必须走 KEYS,传值或控制参数必须走 ARGV,否则集群下直接失败:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- ✅ 正确:
EVAL "return redis.call('GET', KEYS[1])" 1 user:1001——KEYS[1]是真实 key 名,Redis 可预判 slot - ❌ 错误:
EVAL "local k = 'user:' .. ARGV[1]; return redis.call('GET', k)" 0 1001—— 动态拼 key,Redis 无法识别 slot,集群报错CROSSSLOT -
ARGV可传任意字符串,比如ARGV[1]是数量、ARGV[2]是过期时间、ARGV[3]是 JSON 字符串(但建议客户端解析好再传)
脚本里不能做这三类事
一旦踩坑,轻则返回错误,重则阻塞整个 Redis 实例:
- 调用非 Redis 命令的 I/O:如
os.execute、io.open—— 直接报错@user_script:1: attempt to call a nil value - 耗时计算:比如对上万条数据做
for循环 +redis.call("HGET", ...),或用cjson.decode()解析大 JSON - 依赖缺失的 Lua 函数:Redis 内置 Lua 环境删减了
print、math.random、os.time等,调试要用redis.log()
调试脚本时别只看返回值
Redis 不报行号级堆栈,错误信息常是 @user_script:3: 这种模糊提示,且 nil 和 false 都转成空回复,极易误判:
- 先在本地用
lua5.1或luajit跑通逻辑(注意禁用被删函数) - 加日志:
redis.log(redis.LOG_WARNING, "debug: keys="..table.concat(KEYS)),需 Redis 配置loglevel warning以上 - 显式判断返回类型:
if type(res) == "number" then ...,避免把nil当 0 处理
真正容易被忽略的,是脚本出错后前面已执行的命令不会回滚——比如先 INCR 再 GET 报错,计数器已经+1了。业务逻辑得自己兜底,比如用 redis.error_reply() 主动抛错,或设计成“检查前置条件失败就全程不写”。










