
🌈 个人主页: Hygge_Code
🔥 热门专栏:从0开始学习Java | Linux学习| 计算机网络
💫 个人格言: "既然选择了远方,便不顾风雨兼程"

文章目录
- Redis事务解析
-
- [一、Redis事务的核心原理 📖](#一、Redis事务的核心原理 📖)
-
- [1. 事务的三大特性](#1. 事务的三大特性)
- [2. 事务的执行流程](#2. 事务的执行流程)
- [二、Redis事务核心命令实操 🥝](#二、Redis事务核心命令实操 🥝)
-
- [1. 基础命令说明🍋🟩](#1. 基础命令说明🍋🟩)
- [2. 完整实操案例](#2. 完整实操案例)
- [三、Redis事务的错误处理机制 🐦🔥](#三、Redis事务的错误处理机制 🐦🔥)
-
- [1. 组队阶段错误(命令入队失败)](#1. 组队阶段错误(命令入队失败))
- [2. 执行阶段错误(命令入队成功,执行失败)](#2. 执行阶段错误(命令入队成功,执行失败))
- 错误处理总结
- [四、Redis事务的并发控制:乐观锁 vs 悲观锁 🐦🔥🐦🔥🐦🔥](#四、Redis事务的并发控制:乐观锁 vs 悲观锁 🐦🔥🐦🔥🐦🔥)
-
- [1. 悲观锁思路(Redis不原生支持)](#1. 悲观锁思路(Redis不原生支持))
- [2. 乐观锁思路(Redis使用的方案)](#2. 乐观锁思路(Redis使用的方案))
- [3. 乐观锁实战:库存扣减案例](#3. 乐观锁实战:库存扣减案例)
- 五、Redis事务的注意事项
Redis事务解析
一、Redis事务的核心原理 📖
Redis事务的设计核心是"一次性、顺序执行、隔离性 ",本质是将多个命令打包成一个"命令队列",执行过程中不会被其他客户端的命令插入,保证事务内命令的串行执行。
1. 事务的三大特性
与传统关系型数据库的ACID不同,Redis事务只遵循"部分ACID"特性,重点保障:
- 原子性(部分支持):事务内命令要么全部执行,要么全部不执行(仅针对队列组队阶段的错误;执行阶段的错误不会导致回滚);
- 隔离性:事务执行期间,其他客户端的命令无法插入,事务内命令串行执行,不受并发影响;
- 持久性:依赖Redis的持久化机制(RDB/AOF),默认情况下事务本身不提供持久化保障。
2. 事务的执行流程
Redis事务的生命周期分为三个阶段,全程通过MULTI、EXEC、DISCARD三个命令控制:
- 组队阶段 :执行
MULTI命令后,后续所有命令不会立即执行,而是被加入"事务队列",返回QUEUED表示入队成功; - 执行阶段 :执行
EXEC命令后,Redis会按入队顺序依次执行所有命令,返回每个命令的执行结果; - 取消阶段 :执行
DISCARD命令后,事务队列被清空,所有入队命令都不会执行。
流程示意图:
客户端 → MULTI(开启事务)→ 命令1(入队)→ 命令2(入队)→ ... → EXEC(执行队列)/ DISCARD(取消)
二、Redis事务核心命令实操 🥝
Redis事务的命令非常简洁,核心围绕"开启-入队-执行/取消"三个步骤,以下是完整实操示例:
1. 基础命令说明🍋🟩
| 命令 | 作用 | 示例 |
|---|---|---|
MULTI |
开启事务,后续命令入队 | 127.0.0.1:6379> MULTI → OK |
EXEC |
执行事务队列中的所有命令 | 127.0.0.1:6379(TX)> EXEC → 返回命令执行结果列表 |
DISCARD |
取消事务,清空队列 | 127.0.0.1:6379(TX)> DISCARD → OK |
WATCH key [key...] |
监视一个或多个key,若事务执行前key被修改,事务取消 | 127.0.0.1:6379> WATCH balance → OK |
UNWATCH |
取消所有key的监视 | 127.0.0.1:6379> UNWATCH → OK |
2. 完整实操案例
案例1:正常执行事务
redis
# 1. 开启事务
127.0.0.1:6379> MULTI
OK
# 2. 命令入队(设置值、获取值)
127.0.0.1:6379(TX)> SET user:1001 "eric"
QUEUED
127.0.0.1:6379(TX)> SET user:1001:age 25
QUEUED
127.0.0.1:6379(TX)> GET user:1001
QUEUED
# 3. 执行事务(返回3个命令的结果)
127.0.0.1:6379(TX)> EXEC
1) OK
2) OK
3) "eric"
案例2:取消事务(DISCARD)
redis
# 1. 开启事务并入队命令
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET k1 v1
QUEUED
127.0.0.1:6379(TX)> SET k2 v2
QUEUED
# 2. 取消事务,队列清空
127.0.0.1:6379(TX)> DISCARD
OK
# 3. 验证命令未执行
127.0.0.1:6379> GET k1
(nil)
案例3:监视key(WATCH实现乐观锁)
redis
# 客户端A:监视balance键,开启事务
127.0.0.1:6379> SET balance 100
OK
127.0.0.1:6379> WATCH balance
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY balance 20
QUEUED
# 客户端B:在客户端A执行EXEC前,修改balance
127.0.0.1:6379> SET balance 150
OK
# 客户端A:执行事务(因balance被修改,事务取消)
127.0.0.1:6379(TX)> EXEC
(nil)
# 验证结果:balance未被修改
127.0.0.1:6379> GET balance
"150"
三、Redis事务的错误处理机制 🐦🔥
Redis事务的错误分为两种类型,处理方式完全不同,必须重点区分!!!
1. 组队阶段错误(命令入队失败)
原因 :命令语法错误、参数个数错误等(如SET命令缺少参数);
处理逻辑 :事务队列无法正常组建,执行EXEC时会直接返回错误,所有命令都不会执行。
示例:
redis
# 1. 开启事务,入队错误命令(SET缺少value)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET k1
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> SET k2 v2
QUEUED
# 2. 执行事务(因入队错误,事务取消)
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
# 3. 验证:所有命令均未执行
127.0.0.1:6379> GET k2
(nil)
2. 执行阶段错误(命令入队成功,执行失败)
原因 :命令语法正确,但逻辑错误(如对String类型执行INCR自增);
处理逻辑 :Redis不会终止事务执行,错误命令返回执行失败,其他命令正常执行(无回滚机制)。
示例:
redis
# 1. 开启事务,入队逻辑错误的命令(对字符串执行INCR)
127.0.0.1:6379> SET name "eric"
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> INCR name # 逻辑错误:字符串无法自增
QUEUED
127.0.0.1:6379(TX)> SET age 25
QUEUED
# 2. 执行事务(错误命令失败,其他命令成功)
127.0.0.1:6379(TX)> EXEC
1) (error) ERR value is not an integer or out of range # 错误命令返回失败
2) OK # 正常命令执行成功
# 3. 验证:age被正确设置,name未变
127.0.0.1:6379> GET age
"25"
127.0.0.1:6379> GET name
"eric"
错误处理总结
| 错误类型 | 触发时机 | 事务执行结果 | 核心特点 |
|---|---|---|---|
| 组队阶段错误 | 命令入队时 | 全部命令不执行 | 语法错误导致队列无效 |
| 执行阶段错误 | 命令执行时 | 错误命令失败,其他命令正常 | 逻辑错误无回滚 |
四、Redis事务的并发控制:乐观锁 vs 悲观锁 🐦🔥🐦🔥🐦🔥
在高并发场景下,事务可能面临"并发修改"问题(如多个客户端同时操作同一笔余额)。Redis通过WATCH命令实现乐观锁,这是事务并发控制的核心方案。
例子🌰:下面是你的一个银行账户,被你的三个亲属用来消费:(可能出现亲属1先购物,扣了8000块后剩下2000,而亲属2用来支付后,发现账户里的钱变为-4000了,已然透支了额度,他就很奇怪,明明刷卡前的额度是10000,怎么就不够支付了呢?)
1. 悲观锁思路(Redis不原生支持)

- 解释:顾名思义,很悲观,认为并发修改一定会发生,操作前先上锁,阻止其他客户端修改
- 缺点:Redis是单线程模型,上锁会导致性能下降,且容易引发死锁
- 适用场景:无,Redis不推荐使用悲观锁,建议用乐观锁替代
2. 乐观锁思路(Redis使用的方案)

- 思路:很乐观,认为并发修改概率较低,操作前不上锁,仅通过
WATCH监视key;事务执行前检查key是否被修改,若未修改则执行,若已修改则取消事务 - 实现方式:
WATCH命令 + 事务重试机制 - 优点:无锁设计,性能高,适合高并发场景
- 适用场景:秒杀、余额扣减、库存扣减等需要原子操作的场景
3. 乐观锁实战:库存扣减案例
redis
# 初始化库存:10件商品
127.0.0.1:6379> SET stock 10
OK
# 客户端1:监视库存,执行扣减事务
127.0.0.1:6379> WATCH stock
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECR stock
QUEUED
127.0.0.1:6379(TX)> GET stock
QUEUED
# 客户端2:同时执行库存扣减(未监视,直接修改)
127.0.0.1:6379> DECR stock
(integer) 9
# 客户端1:执行事务(因stock被修改,事务取消)
127.0.0.1:6379(TX)> EXEC
(nil)
# 客户端1重试:重新监视并执行
127.0.0.1:6379> WATCH stock
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECR stock
QUEUED
127.0.0.1:6379(TX)> GET stock
QUEUED
127.0.0.1:6379(TX)> EXEC
1) (integer) 8
2) "8" # 重试成功,库存正确扣减
五、Redis事务的注意事项
- 不要依赖事务回滚:执行阶段的错误不会回滚,需在业务代码中预判可能的错误(如先检查数据类型再执行
INCR) - 合理使用
WATCH:监视的key不宜过多,且需在事务执行失败后重试(避免无限重试导致性能问题) - 避免长事务:事务内命令越多,阻塞时间越长,影响Redis吞吐量
如果我的内容对你有帮助,请 点赞 , 评论 , 收藏 。创作不易,大家的支持就是我坚持下去的动力!

