深入理解 Redis 事务:从原理到实践的完整解析

Redis 作为高性能内存数据库,在缓存、计数器、排行榜等场景中被广泛使用。但很多人在使用 Redis 事务时,常常会带着 MySQL 事务的思维方式,从而产生误解:为什么 Redis 事务不支持回滚?为什么事务中读命令会立即执行?WATCH 到底算不算"锁"?

这篇文章将围绕这些问题,系统讲清 Redis 事务的设计理念、底层原理、执行流程以及工程实践中的正确使用姿势,帮助你在面试和真实项目中都能"用对、说清"。


旧版本Redis官网上关于 事务介绍的一个截图


一、Redis 事务与 MySQL 事务的核心差异

在理解 Redis 事务之前,必须先放弃一个错误前提:Redis 事务并不是 MySQL 事务的轻量版。两者的设计目标完全不同。

1. 原子性的本质区别

MySQL 事务追求的是严格的 ACID 原子性:

  • 事务中的多条 SQL 要么全部成功

  • 只要有一条失败,就通过 Undo Log 回滚所有已执行操作

  • 对外呈现为"事务从未发生过"

Redis 事务追求的则是"命令队列级原子性":

  • 保证事务内的命令会被完整地、按顺序执行

  • 执行过程中不会被其他客户端的命令插入

  • 不支持回滚,即使某条命令执行失败,后续命令仍然会继续执行

这并不是 Redis 的"缺陷",而是一个有意为之的取舍 :在 Redis 看来,运行期的命令失败通常是程序 Bug(如类型错误),而不是运行时异常,不应该交给事务系统兜底。


2. 实现机制的不同

MySQL 为了实现事务,需要付出巨大的系统成本:

  • Undo Log 用于回滚

  • Redo Log 用于崩溃恢复

  • 行锁 / 表锁 / MVCC 用于隔离

  • 支持多种隔离级别

Redis 的实现则极度克制:

  • 使用 MULTI / EXEC 缓存命令队列

  • 使用 WATCH 实现乐观锁

  • 所有事务按顺序串行执行

  • 只支持"串行化"这一种隔离语义

这也解释了一个现象:Redis 事务几乎没有额外性能负担


3. 设计目标的差异

|------|------------|-----------|
| 对比维度 | MySQL | Redis |
| 核心目标 | 数据一致性 | 性能与吞吐 |
| 数据模型 | 关系型 | KV / 数据结构 |
| 并发控制 | 悲观锁 + MVCC | 乐观锁 |
| 使用场景 | 核心数据 | 缓存 / 派生数据 |

结论一句话

Redis 事务是为"快"服务的,而不是为"严"服务的。


二、Redis 事务的核心特性与执行流程

1. Redis 事务的四大特性

① 打包执行

MULTI 开启事务后,所有写命令不会立即执行,而是被放入事务队列中。

bash 复制代码
MULTI
SET k1 v1
SET k2 v2

此时 Redis 只会返回 QUEUED,并不会真正修改数据。


② 顺序执行(串行化)

当客户端执行 EXEC 时:

  • Redis 会按命令入队顺序依次执行

  • 中间不会插入其他客户端的命令

  • 整个执行过程是"不可打断"的

这是 Redis 事务最核心的保证。


③ 无回滚机制

如果事务中的某条命令在执行阶段失败:

  • Redis 不会回滚已执行的命令

  • 后续命令仍会继续执行

例如:

bash 复制代码
SET num 10
MULTI
INCR num
LPUSH num 1   # 类型错误
INCR num
EXEC

最终结果是:

  • 第一次 INCR 成功

  • LPUSH 失败

  • 第二次 INCR 仍然执行

这正是 Redis 所谓的"命令原子性而非结果原子性"。


④ 乐观锁控制(WATCH)

Redis 通过 WATCH 提供了一种轻量级并发控制手段,用于解决并发修改问题。


2. Redis 事务的完整执行流程

  1. 客户端发送 MULTI,Redis 进入事务状态

  2. 写命令进入事务队列,返回 QUEUED

  3. (可选)使用 WATCH 监控关键键

  4. 客户端发送 EXEC

    1. 若 WATCH 键未变化:执行事务

    2. 若 WATCH 键已变化:事务终止,返回 nil

  5. 客户端也可使用 DISCARD 主动取消事务


三、WATCH 命令的乐观锁原理与实践

1. WATCH 的底层机制

Redis 为每个键维护一个内部版本号:

  • 键被修改时,版本号递增

  • WATCH 会记录当前版本号快照

  • EXEC 时对比版本号是否发生变化

这本质上就是典型的乐观 实现


2. 转账场景示例

bash 复制代码
WATCH account:1 account:2
GET account:1
GET account:2
MULTI
DECRBY account:1 30
INCRBY account:2 30
EXEC

若执行期间任何一个账户被其他客户端修改,事务将直接失败,由客户端决定是否重试。


3. WATCH 使用中的常见问题

  • 监控键过多:增加版本对比成本,建议 ≤ 10 个

  • 高并发重试风暴:说明业务层存在热点竞争

  • 版本号溢出:理论存在,实践中几乎不可能


四、工程实践中的正确使用姿势

1. 不要在事务中依赖读命令

读命令会立即执行,不受事务保护。

正确做法:

  • 在事务外先读

  • 在事务中只做"基于已知值的写"


2. 哪些场景适合用 Redis 事务

  • 批量写缓存

  • 简单库存扣减

  • 防止竞态条件

不适合:

  • 复杂业务规则

  • 强一致性金融场景


3. 事务 vs Lua 脚本

Lua 脚本:

  • 天生原子执行

  • 支持条件判断和流程控制

  • 性能优于 WATCH + MULTI

复杂逻辑 优先 Lua,事务更多用于简单并发控制。


五、总结

Redis 事务不是关系型数据库事务的替代品,而是一种服务于高性能场景的轻量级原子操作机制

  • MULTI/EXEC 保证命令的顺序与完整执行

  • WATCH 提供乐观锁能力

  • 无回滚是性能与复杂度权衡的结果

真正的 Redis 工程能力,不在于"会不会用事务",而在于:

知道什么时候该用事务,什么时候该用 Lua,什么时候该交给数据库。

理解这一点,你就已经超过了绝大多数"只会背命令"的使用者。

相关推荐
成为大佬先秃头6 小时前
数据库连接池:Druid
数据库·mysql·druid
彭于晏Yan8 小时前
Redisson分布式锁
spring boot·redis·分布式
晓华-warm9 小时前
Warm-Flow 1.8.5 正式发布:超时自动审批、暂存功能来了!
数据库
u01368638210 小时前
将Python Web应用部署到服务器(Docker + Nginx)
jvm·数据库·python
light blue bird10 小时前
多页签Razor组支轴业务整顿组件
数据库·.net·ai大数据·多功能图表报表·web mvc + razor
wregjru10 小时前
【mysql】2.数据表操作
数据库·mysql
手握风云-10 小时前
基于 Java 的网页聊天室(三)
服务器·前端·数据库
LcVong10 小时前
MySQL 5.2/5.7 开启Binlog日志详细步骤(附验证+查看+恢复)
数据库·mysql·adb
FL4m3Y4n11 小时前
MySQL缓存策略
数据库·mysql·缓存
wsx_iot11 小时前
TDengine学习
数据库·学习·tdengine