redis事务仅保证原子性,不支持回滚与隔离;multi→exec需严格三步流程;watch实现乐观锁;集群下须同slot或改用lua脚本。

Redis事务不是传统意义上的ACID事务
Redis的MULTI指令不提供回滚能力,也不保证隔离性——它只是把多个命令打包成一个队列,按顺序执行。一旦某个命令在EXEC阶段报错(比如对string类型执行HGET),该命令失败,其余命令仍会继续执行。所以别指望靠它实现数据库级事务语义。
真正能依赖的只有「原子性」:整个MULTI→EXEC块中的命令,不会被其他客户端的命令穿插执行。这是由Redis单线程模型天然保障的。
正确使用MULTI + EXEC的三步流程
必须严格遵循“开启→排队→提交”节奏,中间不能混入非事务命令,也不能漏掉EXEC或误用DISCARD。
- 先发
MULTI,收到OK响应后,后续每条命令不再立即执行,而是返回QUEUED - 所有要批量执行的命令(如
SET、INCR、LPUSH)依次发送,注意参数顺序和类型必须合法——错误会在EXEC时暴露,而非排队时 - 最后发
EXEC触发执行;若想放弃,发DISCARD清空队列(但已发的MULTI不能撤回)
示例:
MULTI<br>SET user:1001 name "Alice"<br>INCR counter:login<br>LPUSH logs:1001 "login@2024-06-12"<br>EXEC这四条命令会串行执行,无其他客户端命令插入其中。
WATCH配合MULTI才能处理条件更新
如果需要“检查某key值没变才执行后续操作”,单靠MULTI不够——它没有CAS机制。必须用WATCH监控key,在EXEC前做乐观锁校验。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
WATCH要在MULTI之前调用,且只对当前连接有效。一旦被监控的key在EXEC前被其他客户端修改,本次EXEC返回nil,表示事务被放弃。
-
WATCH可以监控多个key,比如WATCH stock:123 price:123 - 如果
EXEC返回nil,应用层需重试整个逻辑(重新WATCH→MULTI→…) -
UNWATCH可手动取消监控,但通常不需要——EXEC或DISCARD后自动失效
常见坑:WATCH后误发了非事务命令(如GET),会导致watch失效,后续EXEC直接执行而不校验。
性能与替代方案的取舍点
MULTI本身开销极小,但大量命令堆积在队列里会增加内存占用和网络往返延迟。更重要的是:它无法解决跨key的复杂逻辑判断(比如“当A>10且B不存在时才SET C”),这种场景Lua脚本更合适。
- Lua脚本在Redis中是原子执行的,支持if/else、循环、本地变量,还能用
redis.call()调用任意命令 - 脚本首次运行会被缓存,后续用
EVALSHA更快;但调试比纯命令难,且需注意random、time等不可预测函数禁止使用 - 如果只是简单批量写入(如日志记录、计数器累加),
MULTI够用;涉及条件分支或多次读取判断,优先写Lua
容易被忽略的一点:Redis集群模式下,MULTI只能操作同一个slot里的key。跨slot的key会直接报错CROSSSLOT Keys in a transaction don't hash to the same slot——这时要么改key设计(用{}确保同slot),要么切到单机模式,要么用Lua+redis.call()兜底。










