redis事务(multi/exec)不支持分布式原子性与回滚,仅单实例有效;eval lua脚本才是唯一可靠原子封装方式,需用redis.call、keys/argv传参、显式return终止分支,并配合evalsha预加载提升性能。

Redis事务命令本身不支持真正的分布式事务
直接用 MULTI/EXEC 无法跨节点保证原子性,更不能回滚已执行的命令。哪怕加了 WATCH,也只在单实例内起作用,且只能检测 key 变更,无法做业务逻辑判断或条件中止。一旦某个命令因值不满足预期而失败(比如库存为负),后续命令照常执行——这不是事务,是“命令队列批量提交”。
EVAL 执行 Lua 脚本才是实际可行的原子封装方式
Redis 对每个 EVAL 请求的整个脚本做原子执行:期间不会被其他客户端打断,也不会部分成功。这是实现“检查-修改-反馈”闭环的唯一可靠路径。
- 脚本内必须用
redis.call()调用 Redis 命令,不能用redis.pcall()(除非你明确要捕获错误并手动处理) -
KEYS[1]和ARGV[1]是唯一安全传参方式;不要在脚本里拼接字符串构造 key 或 value - 所有分支逻辑(如“库存不足则退出”)必须显式用
return终止,否则脚本会继续往下跑 - 避免在脚本里做耗时操作(比如循环上万次、调用外部 HTTP),否则会阻塞整个 Redis 事件循环
示例:安全扣减库存
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
local stock = tonumber(redis.call('GET', KEYS[1]) or '0')
local need = tonumber(ARGV[1])
if stock <h3>跨多个 Redis 实例时 Lua 无法自动解决分布式事务</h3><p>一个 <code>EVAL</code> 只能操作当前连接的 Redis 实例(或 Cluster 中的一个 slot)。如果你的订单数据在 A 实例、库存数据在 B 实例,Lua 脚本本身无法直连两个实例——Redis 不提供跨实例的原子协调能力。</p>
- 所谓“用 Lua 实现分布式事务”,本质是把原本需要多节点协调的逻辑,压缩到单节点可完成的范围内(例如:只管库存,订单状态由应用层异步补偿)
- 真要跨实例,得靠外部协调者(如 Seata、XA 协议代理)或最终一致性方案(消息队列 + 本地事务表),而不是指望 Lua
- Cluster 模式下,
KEYS必须落在同一 slot,否则EVAL直接报错CROSSSLOT Keys in request don't hash to the same slot
生产环境必须用 EVALSHA + SCRIPT LOAD 预加载脚本
每次发大段 Lua 字符串走 EVAL,不仅浪费带宽,还让 Redis 重复解析编译,影响性能。正确做法是:
- 启动时用
SCRIPT LOAD "..."得到 SHA1 值(如"98e7...a2f") - 运行时只传
EVALSHA 98e7...a2f 1 mykey 10 - 若返回
NOSCRIPT,说明脚本被驱逐,需 fallback 到EVAL并重载
这个细节线上容易被忽略,但高并发下脚本反复加载会明显拖慢响应。










