Redis原子性需用Lua脚本实现,因MULTI/EXEC不保证中间状态隔离;脚本通过EVAL单线程执行,KEYS和ARGV须规范传参,集群下多slot键需拆分处理,分布式锁须SET+NX+EX原子设值并用Lua校验解锁,SCRIPT LOAD+EVALSHA需容错NOSCRIPT。

用 Lua 脚本封装多步操作,Redis 才认它是原子的
Redis 本身没有“事务块内自动加锁”这回事,EXEC 只保证命令按序执行、不被其他客户端插入,但不保证中间状态不被读取或修改。真正实现复杂逻辑原子性,得靠 EVAL 或 EVALSHA 运行 Lua 脚本——脚本在服务端单线程执行,期间不会被中断或穿插其他命令。
常见错误是以为 MULTI/EXEC 能搞定条件更新、计数+过期+通知这类组合操作。比如“库存大于 0 才扣减并记录日志”,用事务根本拦不住并发超卖——因为 GET 和 DECR 是两轮网络往返,中间已被抢先改掉。
- 脚本必须把所有读、判、写逻辑写进一个
EVAL调用里,不能拆成多次请求 - 用
redis.call()调用 Redis 命令,别用redis.pcall()除非你真要捕获异常并手动处理(它不抛错,只返回状态表) - 脚本里不能用随机、时间、外部 I/O 等非确定性操作,否则主从复制可能不一致
- 脚本长度建议控制在几百行内;太长会阻塞事件循环,影响其他请求
EVAL "if redis.call('GET', KEYS[1]) > '0' then redis.call('DECR', KEYS[1]); return 1 else return 0 end" 1 inventory:sku123
KEYS 和 ARGV 怎么传才不踩坑
Lua 脚本里不能直接拼接 key 名,否则无法被 Redis 集群路由识别,也会触发 EVAL 安全限制。所有 key 必须显式通过 KEYS 数组传入,非 key 数据(如阈值、新值)走 ARGV。
典型翻车场景:脚本里写 redis.call('GET', 'user:'..ARGV[1]),看着能跑,但在集群模式下直接报错 (error) MOVED;或者用 ARGV 传 key 名试图绕过校验,结果脚本被拒绝执行。
-
KEYS参数个数必须和脚本里KEYS[n]的最大索引一致,少传会报ERR Error running script - 同一个脚本里多个 key,必须全部列在
KEYS里,不能一部分硬编码、一部分拼接 - 如果逻辑涉及不同 slot 的 key(比如用户信息和订单日志),Redis 集群下无法用单个脚本完成——得拆成多个原子操作,或改用单节点部署
为什么不用 SETNX + EXPIRE 组合做分布式锁
单独用 SETNX 加锁再 EXPIRE 设过期,不是原子的。万一进程在 SETNX 成功后崩溃或网络断开,EXPIRE 没发出去,锁就永远卡死。
更隐蔽的问题是锁误删:A 拿到锁,但执行超时被自动释放;B 接着拿到锁;A 结束后却调用 DEL 删除了 B 的锁——因为没校验锁的拥有者。
- 正确做法是用
SET key random_value EX seconds NX一条命令完成设值+过期+原子获取 - 解锁必须用 Lua 脚本:先
GET校验 value 是否匹配,再DEL,整个过程不可分割 - 不要依赖系统时间做锁续期,要用
PEXPIRE基于 Redis 服务端时间重设 TTL
EVAL "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end" 1 mylock 8f4d2a9e
脚本加载与缓存:SCRIPT LOAD 和 EVALSHA 的实用节奏
每次用 EVAL 发送完整脚本,不仅浪费带宽,还会让 Redis 重复解析编译。高频调用的脚本应该先 SCRIPT LOAD 得到 SHA1,后续全用 EVALSHA。
但别想当然认为“加载一次永久有效”。Redis 重启、主从切换、SCRIPT FLUSH 都会让 SHA1 失效,直接 EVALSHA 会返回 NOSCRIPT 错误。
- 生产代码必须对
EVALSHA做容错:捕获NOSCRIPT后 fallback 到EVAL重发脚本 - 脚本内容变更时,SHA1 自动变化,旧缓存自然失效,这是好事——不用手动管理版本
- Spring Data Redis 等客户端通常内置了这个 fallback 逻辑,但自己写 HTTP 或 RESP 调用时得手写
最常被忽略的一点:Lua 脚本里没有“回滚”概念。一旦某条 redis.call() 报错,整个脚本终止,前面已执行的命令不会撤销。设计时得预判失败路径,要么确保每步都幂等,要么在脚本外补监控和修复机制。










