Redis的事务操作


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

文章目录

  • Redis事务解析
    • [一、Redis事务的核心原理 📖](#一、Redis事务的核心原理 📖)
      • [1. 事务的三大特性](#1. 事务的三大特性)
      • [2. 事务的执行流程](#2. 事务的执行流程)
    • [二、Redis事务核心命令实操 🥝](#二、Redis事务核心命令实操 🥝)
    • [三、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事务的生命周期分为三个阶段,全程通过MULTIEXECDISCARD三个命令控制:

  1. 组队阶段 :执行MULTI命令后,后续所有命令不会立即执行,而是被加入"事务队列",返回QUEUED表示入队成功;
  2. 执行阶段 :执行EXEC命令后,Redis会按入队顺序依次执行所有命令,返回每个命令的执行结果;
  3. 取消阶段 :执行DISCARD命令后,事务队列被清空,所有入队命令都不会执行。

流程示意图:

复制代码
客户端 → MULTI(开启事务)→ 命令1(入队)→ 命令2(入队)→ ... → EXEC(执行队列)/ DISCARD(取消)

二、Redis事务核心命令实操 🥝

Redis事务的命令非常简洁,核心围绕"开启-入队-执行/取消"三个步骤,以下是完整实操示例:

1. 基础命令说明🍋‍🟩

命令 作用 示例
MULTI 开启事务,后续命令入队 127.0.0.1:6379> MULTIOK
EXEC 执行事务队列中的所有命令 127.0.0.1:6379(TX)> EXEC → 返回命令执行结果列表
DISCARD 取消事务,清空队列 127.0.0.1:6379(TX)> DISCARDOK
WATCH key [key...] 监视一个或多个key,若事务执行前key被修改,事务取消 127.0.0.1:6379> WATCH balanceOK
UNWATCH 取消所有key的监视 127.0.0.1:6379> UNWATCHOK

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吞吐量

如果我的内容对你有帮助,请 点赞 , 评论 , 收藏 。创作不易,大家的支持就是我坚持下去的动力!

相关推荐
堕2742 小时前
MySQL数据库《基础篇--库的操作和数据类型》
数据库·mysql
Web极客码2 小时前
解决WordPress迁移后重定向到旧域名的问题
数据库·wordpress·网站迁移
Web打印4 小时前
Phpask(php集成环境)之15 phpstudy转移到phpask
数据库·mysql
LaughingZhu10 小时前
Product Hunt 每日热榜 | 2026-02-14
数据库·人工智能·经验分享·神经网络·搜索引擎·chatgpt
软件派10 小时前
近两年国外主流数据库深度解析:从技术特性到场景适配
数据库
Elastic 中国社区官方博客11 小时前
DevRel 通讯 — 2026 年 2 月
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·jina
学到头秃的suhian12 小时前
Redis消息队列
数据库·redis·缓存
野犬寒鸦12 小时前
从零起步学习并发编程 || 第九章:Future 类详解及CompletableFuture 类在项目实战中的应用
java·开发语言·jvm·数据库·后端·学习
爆米花byh12 小时前
在RockyLinux9环境的Doris单机版安装
linux·数据库·database