Redis通过单线程模型实现事务“不被打断”:MULTI开启后命令入队暂存,EXEC触发主线程串行执行队列中所有命令,期间不处理其他客户端请求,从而保证执行过程的原子性与隔离性。

Redis 的 MULTI/EXEC 机制本质是命令队列+单线程串行执行,它能保证“执行不被插队”,但不是传统数据库意义上的事务。
它怎么做到“不被打断”?
Redis 是单线程模型。当客户端发送 MULTI 后,后续命令不会立即执行,而是被暂存到该客户端专属的命令队列中;直到 EXEC 发出,Redis 主线程才一次性、按顺序把队列里所有命令逐个执行完,期间不处理其他客户端的任何请求。这就实现了“执行过程的串行不可分割”——这是它原子性的来源。
- MULTI 开启事务,之后每条命令返回 QUEUED,表示入队成功
- EXEC 触发批量执行,返回一个结果数组,每个元素对应一条命令的执行结果
- DISCARD 可随时清空队列,放弃整个事务
为什么说它“不支持真正的原子性”?
因为没有回滚能力。如果队列中某条命令执行失败(比如对 string 类型执行 INCR、key 不存在却执行 HGET),Redis 会记录错误并继续执行后续命令。已成功的操作无法撤销,最终可能出现“部分成功”的状态。
- 例如:MULTI → SET a 1 → INCR b(b 是字符串)→ SET c 2 → EXEC,结果是 a 和 c 被设置,b 报错但不影响 c
- 开发者必须在应用层检查 EXEC 返回结果,识别哪些命令失败,并自行决定是否重试或补偿
WATCH 是怎么补救并发问题的?
WATCH 提供乐观锁机制,用于解决“读-改-写”场景下的竞态。它监控一个或多个 key,若在 MULTI 到 EXEC 之间这些 key 被其他客户端修改过,EXEC 就会直接返回 nil,整组命令都不执行。
- 典型用法:先 WATCH balance,再 GET 获取当前值,计算新值后 MULTI/EXEC 更新
- EXEC 返回 nil 表示监视失败,需重新尝试整个流程(通常配合循环或重试逻辑)
- UNWATCH 可主动取消监视;WATCH 在 EXEC 或 DISCARD 后自动失效
它不解决哪些问题?
Redis 事务不提供 ACID 中的强一致性保障和持久性承诺。它不校验命令逻辑合法性(如类型错误只在运行时暴露),也不依赖磁盘落盘——即使开启了 AOF/RDB,事务执行中的数据丢失风险仍存在。跨节点操作(如 Redis Cluster 中涉及多个 slot 的命令)也无法通过 MULTI/EXEC 保证原子性,此时应改用 Lua 脚本。
- 不替代数据库事务:不适合强一致性要求高的核心业务(如金融转账主流程)
- 不处理网络中断或客户端崩溃:EXEC 前断连,队列就丢失;EXEC 后无响应,需靠超时或幂等设计兜底
- 不支持嵌套事务或条件分支:整个事务块是扁平、静态的命令序列


















