ShineWinsu头像
关注
对于Redis:事务的解析封面图

对于Redis:事务的解析

开篇介绍:

hello 大家,本篇博客我们来学习Redis的事务,那么我们之前MySQL就已经学习过了事务,也就是MVCC和ACID,那么我们本篇博客就来看看,Redis的事务又是如何的呢?

第一节 Redis 事务是什么

一、先搞懂:什么叫「事务」?

不管是 Redis、MySQL、Oracle、SQL Server,只要在计算机、数据库、缓存领域提到事务这两个字,本质上都是一个完全统一、完全通用、完全不变的意思:

把好几个独立的、连续的、有逻辑关联的操作,打包绑定成「一整组不可拆分、不可打断、不可捣乱的动作集合」,这一组动作要么从头到尾一起全部做完,要么就从一开始一个都别做、一个都不执行,绝对不能出现「做了一半、剩下一半没做、中间被别人插一脚、一半成功一半失败」的混乱、错误、非法情况,保证数据的安全、逻辑的正确、业务的稳定。

举一个生事务例子:你去商场的自动取款机(ATM)取钱,完成一次取钱操作,一共要做 3 件必须绑定在一起的核心事情:

  1. 验证你的银行卡密码(系统确认是本人操作,防止盗刷)
  2. 从你的银行卡账户里扣掉你要取的对应金额(账户金额减少)
  3. 从取款机的出钞口吐出你要取的钞票(拿到现金)

这 3 件事,就是一个标准的、完美的、符合事务定义的真实事务:

  • 如果密码输错 → 3 件事全部取消,不扣钱、不吐钱,回到初始状态;
  • 如果扣钱成功、但取款机突然坏了吐不出钞票 → 银行系统必须自动把扣掉的钱退回到你的银行卡里(这就是事务的回滚);
  • 绝对不能出现「扣了你的钱,却没吐钞票」「吐了钞票,却没扣钱」这种半拉子、错误、不合理的情况。

这就是事务存在的唯一意义:保证一组有逻辑关联的操作的完整性、安全性、正确性,不出任何乱子、不产生任何错误数据、不引发任何业务故障。


二、Redis 事务 和 你熟悉的 MySQL 事务 —— 天差地别!

你如果学过关系型数据库 MySQL,一定背过滚瓜烂熟的 事务四大核心特性 ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

但是:Redis 事务,根本不是完整意义上的标准事务!它是「阉割版、弱化版、简易版、基础版」的事务!Redis 只保留了事务「最基础、最底层、最简单的一点点功能」,剩下 MySQL 拥有的所有强大、安全、严谨的事务特性,Redis 全都没有!一个都没有!

1. 原子性(最关键区别!直接决定了 Redis 事务的底线和上限)
  • MySQL 事务的原子性(标准、完美、严谨):要么全部成功,要么全部失败,失败自动回滚到事务开始前的初始状态!就像 ATM 取钱:扣钱失败 → 钱自动退回银行卡,绝对不会出现「钱扣了、东西没拿到」「操作一半失败、数据错乱」的情况。
  • Redis 事务的原子性(弱化、残缺、无回滚):没有回滚!没有回滚!没有回滚!重要的事情说一百遍!Redis 只能保证:这一组命令按顺序、连续执行,不被别人的命令插队、打断。如果中间有一条命令执行失败了,前面已经执行成功的命令,不会撤销、不会恢复、不会变回原来的样子、不会回滚!错了就是错了,烂摊子必须你自己手动收拾,Redis 完全不管、完全不处理、完全不负责!
2. 一致性(数据合不合法、规不规范、合不合理的问题)
  • MySQL 一致性(严格、约束、合规):事务执行完成后,数据一定是合法的、符合规则的、合理的。比如:银行卡余额不能是负数、学生成绩不能是负数、商品库存不能是负数,MySQL 会通过约束、触发器、事务规则严格限制,绝对不会出现非法数据。
  • Redis 一致性(无约束、无规则、无限制):没有任何约束,没有任何一致性保证!完全自由,完全不管!你把用户余额改成 -10000、-100000、-1000000,把商品库存改成 -999,Redis 完全不管,它只管执行你发的命令,不管数据合不合法、合不合理、符不符合业务规则。
3. 隔离性(多个人同时操作数据,会不会打架、会不会冲突、会不会错乱)
  • MySQL 隔离性(复杂、严谨、多层级):有 4 个标准隔离级别(读未提交、读已提交、可重复读、串行化),专门解决「多个人、多个事务同时改同一个数据」的并发冲突问题,防止脏读、不可重复读、幻读、数据错乱。
  • Redis 隔离性(不需要、不存在、无级别):根本不需要隔离性!完全不存在隔离级别!因为 Redis 是单线程工作模式!同一时间,Redis 只能执行一个命令,或者一组事务,不可能有「两个事务同时跑」「两个命令同时执行」的情况,所以天然不会打架、不会冲突、不会错乱,不需要任何隔离级别。
4. 持久性(数据会不会永久保存、断电会不会丢)
  • MySQL 持久性(永久、落盘、安全):事务一提交,数据立刻写到物理硬盘里,永久保存,就算服务器关机、断电、重启、崩溃,数据也不会丢、不会消失。
  • Redis 持久性(无关、分离、独立):事务和持久化完全没关系!一毛钱关系都没有!Redis 数据默认存在内存里,断电就丢、重启就没。是否把数据存到硬盘,是 Redis 持久化机制(AOF 追加文件、RDB 快照)的功能,和事务本身完全独立、完全分离,事务只管「修改内存数据」,不管「数据是否存硬盘」。

三、Redis 事务的真正底层本质

Redis 事务,就是一个「专属的命令暂存队列」:

  1. 你对 Redis 输入 MULTI → 相当于跟快递站工作人员说:「我接下来要寄好几个包裹,先帮我暂存在我专属的仓库里,别先派送!别先发货!」
  2. 你发 set、incr、del、hset 等数据操作命令 → 相当于把包裹一个个放进快递站你的专属仓库,Redis 会回复 QUEUED,意思是「包裹已入队,暂存成功,等待统一派送」;
  3. 你对 Redis 输入 EXEC → 相当于跟快递员说:「现在把我仓库里所有包裹,一次性、按顺序、全部派送出去!一个都别落下!」
  4. 你对 Redis 输入 DISCARD → 相当于跟快递员说:「这些包裹我不寄了,全部扔掉、全部清空,一个都别派送!一个都别执行!」

Redis 事务,在整个 Redis 体系里,唯一能保证的事情,只有这一件、就这一件、仅此一件:事务里的所有命令,会按你入队的顺序、连续、一次性、不间断执行,中间绝对不会被其他客户端的命令插队、打断、插入、捣乱!仅此而已!仅此而已!仅此而已!没有其他任何保证!没有其他任何特性!没有其他任何功能!


第二节 Redis 事务 5 大核心命令

Redis 事务一共就 5 个核心命令,MULTI、EXEC、DISCARD、WATCH、UNWATCH


一、MULTI:开启事务(标记:接下来的命令全部暂存入队,不立即执行)

1. 命令作用

告诉 Redis 服务器:从现在开始,我发的所有数据修改、数据操作命令,先不要执行、先不要生效、先不要改数据,全部放进我这个客户端专属的事务队列里暂存起来!等我后续发命令,再统一处理!

2. 执行流程
  1. 客户端在 Redis 命令行输入 MULTI 命令,发送给 Redis 服务器;
  2. 服务器接收到命令后,给当前客户端打上「事务模式开启」的专属标记;
  3. 服务器给当前客户端创建一个空的、专属的、独立的事务队列(每个客户端的队列都是独立的,互不干扰、互不影响);
  4. 服务器处理完成,返回 OK 给客户端,表示事务开启成功,后续命令全部入队暂存。
3. 完整实例
127.0.0.1:6379> MULTI
OK
  • 第一行:你向 Redis 服务器发送「开启事务」的命令,告诉 Redis 要开始打包命令了;
  • 第二行:Redis 服务器回复 OK,代表事务模式已成功开启,后续所有数据操作命令全部进入事务队列暂存,不执行、不生效。
4. 关键注意点
  • 一个客户端同一时间只能开启一个事务,绝对不能嵌套 MULTI(比如在事务里再输 MULTI),会直接报错;
  • 开启事务后,只有数据操作命令(set、incr、del、hset、lpush 等)会入队,info、config、client等系统命令会直接执行,不进入事务队列;
  • 不同客户端的事务完全独立,客户端 A 开事务,不影响客户端 B 的正常命令执行,互不干扰。
5. 错误示范(嵌套 MULTI,直接报错)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> MULTI
(error) ERR MULTI calls can not be nested
  • 报错原因:Redis 事务不支持嵌套,一个客户端只能开一个事务。

二、EXEC:执行事务(一次性跑完队列里所有命令,按顺序生效)

1. 命令作用

告诉 Redis 服务器:别暂存了!别等了!现在立刻、马上、按照我入队的顺序,执行我刚才放进事务队列的所有命令!全部生效!全部执行!

2. 执行流
  1. 客户端在 Redis 命令行输入 EXEC 命令,发送给 Redis 服务器;
  2. 服务器接收到命令后,从头到尾、按顺序遍历当前客户端的事务队列,一条一条执行队列里的命令;
  3. 服务器把每条命令的执行结果,按入队顺序打包成一个结果列表,返回给客户端;
  4. 所有命令执行完成后,服务器自动清空事务队列,关闭事务模式;
  5. 后续客户端发送的命令,恢复正常模式,立即执行,不再进入事务队列暂存。
3. 完整实例
# 第一步:开启事务,进入命令入队暂存模式
127.0.0.1:6379> MULTI
OK

# 第二步:发送第一条string类型命令,存入事务队列
127.0.0.1:6379> set k1 1
QUEUED

# 第三步:发送第二条string类型命令,存入事务队列
127.0.0.1:6379> set k2 2
QUEUED

# 第四步:发送第三条string类型命令,存入事务队列
127.0.0.1:6379> set k3 3
QUEUED

# 第五步:发送自增命令,给k1加1,存入事务队列
127.0.0.1:6379> incr k1
QUEUED

# 第六步:执行事务,一次性运行所有队列命令
127.0.0.1:6379> EXEC
1) OK
2) OK
3) OK
4) (integer) 2
4. 返回结果详细解释
  • 1) OK:第一条命令 set k1 1 执行成功,k1 的值设为 1;
  • 2) OK:第二条命令 set k2 2 执行成功,k2 的值设为 2;
  • 3) OK:第三条命令 set k3 3 执行成功,k3 的值设为 3;
  • 4) (integer) 2:第四条命令 incr k1 执行成功,k1 从 1 自增为 2。
5. 结果验证
# 查看 k1 的值,是事务里自增后的 2
127.0.0.1:6379> get k1
"2"

# 查看 k2 的值,是事务里设置的 2
127.0.0.1:6379> get k2
"2"

# 查看 k3 的值,是事务里设置的 3
127.0.0.1:6379> get k3
"3"

所有命令全部按顺序生效,没有被打断,没有被插队,没有出现任何错乱。

6. 多数据类型事务实例(hash、list、set,扩充内容)
# 开启事务
127.0.0.1:6379> MULTI
OK

# hash类型操作
127.0.0.1:6379> hset user:1 name zhangsan age 20
QUEUED

# list类型操作
127.0.0.1:6379> lpush list1 a b c
QUEUED

# set类型操作
127.0.0.1:6379> sadd set1 1 2 3
QUEUED

# 执行事务
127.0.0.1:6379> EXEC
1) (integer) 2
2) (integer) 3
3) (integer) 3

# 验证结果
127.0.0.1:6379> hgetall user:1
1) "name"
2) "zhangsan"
3) "age"
4) "20"
127.0.0.1:6379> lrange list1 0 -1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> smembers set1
1) "1"
2) "2"
3) "3"

三、DISCARD:放弃事务(清空队列・所有命令不执行・不生效)

1. 命令作用

告诉 Redis 服务器:我后悔了!我不想执行这个事务了!把事务队列里的所有命令全部清空、全部删除、全部扔掉,一条都不要执行!一条都不要生效!

2. 执行流程
  1. 客户端在 Redis 命令行输入 DISCARD 命令,发送给 Redis 服务器;
  2. 服务器接收到命令后,直接清空当前客户端的事务队列,删除所有暂存的命令;
  3. 服务器自动关闭事务模式,恢复正常命令执行模式;
  4. 服务器返回 OK 给客户端,表示事务放弃成功,队列已清空。
3. 完整实例
# 第一步:开启事务,进入命令入队模式
127.0.0.1:6379> MULTI
OK

# 第二步:发送命令,存入事务队列
127.0.0.1:6379> set k1 1
QUEUED

127.0.0.1:6379> set k2 2
QUEUED

127.0.0.1:6379> set k3 3
QUEUED

# 第三步:放弃事务,清空队列,所有命令不执行
127.0.0.1:6379> DISCARD
OK

# 第四步:验证结果,所有命令都没执行,key都不存在
127.0.0.1:6379> get k1
(nil)

127.0.0.1:6379> get k2
(nil)

127.0.0.1:6379> get k3
(nil)
  • (nil) 代表 key 完全不存在,说明事务里的所有命令完全没有执行、完全没有生效。
4. 关键注意点
  • DISCARD 只能放弃还没执行的事务(还没发送 EXEC 命令);
  • 如果已经执行了 EXEC,再发送 DISCARD 会直接报错:(error) ERR DISCARD without MULTI;
  • 执行 DISCARD 后,事务模式自动关闭,后续命令立即执行,不再暂存。
5. 错误示范(已执行 EXEC 再 DISCARD,直接报错)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 1
QUEUED
127.0.0.1:6379> EXEC
1) OK
127.0.0.1:6379> DISCARD
(error) ERR DISCARD without MULTI

四、WATCH:监控 Key(解决并发修改错乱・Redis 简易乐观锁・最核心安全命令)

1. 为什么必须用 WATCH?

我们先看不用 WATCH,两个客户端同时改同一个 key,会出现什么业务问题:

双客户端并发场景(无 WATCH,数据错乱)

【时间 1】客户端 1 开启事务,准备修改 key,命令入队

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key 100
QUEUED

客户端 1 只是把命令放入事务队列,还没执行 EXEC!还没生效!

【时间 2】客户端 2 直接修改 key,命令立即执行、立即生效

127.0.0.1:6379> set key 200
OK

【时间 3】客户端 1 执行事务,命令生效

127.0.0.1:6379> EXEC
1) OK

【最终结果】

127.0.0.1:6379> get key
"100"

错误出现了:从时间先后顺序看:客户端 1 先写命令 → 客户端 2 后写命令,理应最终值是 200!但实际结果是 100这就是并发修改导致的致命数据错误,必须用 WATCH 命令彻底解决!

2. WATCH 是什么?

WATCH 命令就是给指定的 key 加一个 **「24 小时监控哨、数据保镖」**,规则只有一条:

在事务执行(EXEC)之前,如果被监控的 key 被其他客户端修改过、改动过、变化过,本次事务直接彻底作废、彻底失败,所有命令一条都不执行、一条都不生效!

3. WATCH 底层原理

Redis 内部给每一个存在的 key,都默默维护了一个看不见、摸不着、不用你操作的版本号:

  • key 每被修改一次(set、incr、del、hset 等),版本号自动 +1;
  • 执行 WATCH key 时,Redis 会记录当前 key 的版本号(比如 0);
  • 执行 EXEC 时,Redis 自动对比「监控时的版本号」和「当前最新版本号」:① 版本号完全一样 → 没人修改,正常执行事务;② 版本号不一样 → 被别人改过,事务直接失败,返回 nil。
4. WATCH 完整双客户端实例
第一步:客户端 1 监控 k1(Redis 记录 k1 当前版本号 = 0)
127.0.0.1:6379> WATCH k1
OK
第二步:客户端 1 开启事务,命令入队(暂存,不执行)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 100
QUEUED
127.0.0.1:6379> set k2 1000
QUEUED

注意:只是入队,还没执行!还没生效!

第三步:客户端 2 修改 k1(k1 版本号从 0 自动变成 1)
127.0.0.1:6379> set k1 200
OK
第四步:客户端 1 执行事务(版本号不一致,事务彻底失败)
127.0.0.1:6379> EXEC
(nil)
  • (nil) 代表事务彻底失败、彻底作废,所有命令一条都没执行、一条都没生效!
第五步:验证最终结果(数据正确,无错乱)
# k1 是客户端 2 修改的 200,客户端 1 的命令完全没生效
127.0.0.1:6379> get k1
"200"

# k2 不存在,客户端 1 的事务完全作废
127.0.0.1:6379> get k2
(nil)
5. WATCH 监控多个 key 实例
# 客户端1监控k1、k2两个key
127.0.0.1:6379> WATCH k1 k2
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 100
QUEUED
127.0.0.1:6379> set k2 200
QUEUED

# 客户端2修改k2
127.0.0.1:6379> set k2 300
OK

# 客户端1执行事务,失败
127.0.0.1:6379> EXEC
(nil)
6. WATCH 关键注意点
  • WATCH 是一次性的:执行 EXEC 或 DISCARD 后,监控自动取消,下次需要重新 WATCH;
  • 可以同时监控多个 key:WATCH k1 k2 k3 k4,只要有一个 key 被改,事务就失败;
  • 只有其他客户端修改 key 会触发失效,当前客户端事务内修改不会触发(因为命令还没执行);
  • WATCH 只监控当前数据库(select 0)的 key,跨库监控无效。

五、UNWATCH:取消监控(解除 WATCH 的所有监控,一次性清空)

1. 命令作用

取消当前客户端对所有 key 的监控,不管之前监控了多少个 key、多少组 key,一次性全部解除、全部清空、全部失效!

2. 使用场景
  • 不想监控了,重新开始事务;
  • 事务执行前,清空所有监控,避免误触发;
  • 监控错 key,取消后重新监控。
3. 完整实例
# 监控 k1、k2、k3 三个 key
127.0.0.1:6379> WATCH k1 k2 k3
OK

# 取消所有监控,一次性清空
127.0.0.1:6379> UNWATCH
OK

# 现在其他客户端修改 k1,不会影响你的事务
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 100
QUEUED
127.0.0.1:6379> EXEC
1) OK

# 验证结果,事务执行成功
127.0.0.1:6379> get k1
"100"
4. 关键注意点
  • UNWATCH 只取消当前客户端的监控,不能取消其他客户端的监控;
  • 没有监控任何 key 时,执行 UNWATCH 不会报错,返回 OK;
  • UNWATCH 执行后,事务模式不受影响,只是监控失效。

第三节 Redis 事务的两种错误(最核心!决定了无回滚特性)

Redis 事务执行时,只会出现两种错误

一、错误 1:入队时语法错误(命令写错,Redis 不认识、看不懂)

1. 错误场景(命令拼写错误、语法错误)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 1
QUEUED
# 命令写错,sssset 不是 Redis 命令,语法错误、拼写错误
127.0.0.1:6379> sssset k2 2
(error) ERR unknown command 'sssset'
127.0.0.1:6379> EXEC
# 整个事务直接作废,不执行任何命令
(error) EXECABORT Transaction discarded because of previous errors.
2. 最终结果(绝对唯一)

整个事务全部作废、全部取消,一条命令都不执行、一条命令都不生效!

二、错误 2:执行时逻辑错误(命令语法对,但运行失败、逻辑错误)

1. 错误场景(命令正确,但是数据类型不匹配、逻辑错误)
127.0.0.1:6379> MULTI
OK
# 设置 k1 为字符串 hello(正确)
127.0.0.1:6379> set k1 hello
QUEUED
# 给字符串执行自增,逻辑错误(只有数字能自增)
127.0.0.1:6379> incr k1
QUEUED
# 设置 k2 为数字 2(正确)
127.0.0.1:6379> set k2 2
QUEUED
# 执行事务
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
2. 最终结果(绝对唯一,无回滚)

错误的那一条命令失败,其他命令全部正常执行、正常生效!没有回滚!没有恢复!没有撤销!

  • k1 = hello(成功生效)
  • incr k1(执行失败)
  • k2 = 2(成功生效)

这就是 Redis 没有回滚 的铁证!永远记住!


第四节 Redis 事务 30 条生产级注意事项

  1. 每个客户端有独立的、专属的事务队列,互不干扰、互不影响、互不冲突;
  2. 事务只在当前客户端生效,其他客户端的命令、事务完全不受影响;
  3. WATCH 监控是一次性的,执行EXEC/DISCARD后,监控自动取消,必须重新 WATCH;
  4. Redis 事务绝对不能嵌套,不能在事务里再执行MULTI,会直接报错;
  5. Redis 是单线程工作模式,事务天然不会被其他命令插队、打断、插入;
  6. Redis 事务不支持回滚,这是 Redis 官方设计,不是 bug,不是故障;
  7. EXEC是事务唯一执行入口,不发EXEC,事务永远不执行、永远不生效;
  8. DISCARD只能放弃未执行的事务,已经执行EXEC的事务无法撤销、无法回滚;
  9. 高并发场景下,WATCH会导致大量事务失败,不适合强并发核心业务;
  10. 事务中绝对不要放耗时命令(如keys *、hgetall大 hash),会阻塞整个 Redis;
  11. 事务只操作内存数据,和 Redis 持久化(AOF/RDB)完全无关、完全分离;
  12. 语法错误→全事务作废,逻辑错误→单条失败,其他执行,这是铁律;
  13. 开启事务后,系统命令(info、config)直接执行,不进入事务队列;
  14. WATCH 监控的 key 必须在当前数据库,跨库监控无效,不会生效;
  15. 事务执行成功后,队列自动清空,无需手动处理、手动删除;
  16. 放弃事务后,事务模式自动关闭,后续命令立即执行,不再暂存;
  17. 多客户端同时操作,只有事务执行时会独占 Redis,其他时间互不影响;
  18. Redis 事务没有锁机制,WATCH只是简易乐观锁,不是分布式锁;
  19. 金融转账、支付充值、秒杀库存等严格安全场景,绝对不要用 Redis 事务;
  20. 严格安全场景,推荐使用 Redis Lua 脚本,实现真正的原子性;
  21. 事务的唯一价值:保证一组命令连续执行,不被其他客户端插队、打断;
  22. 事务执行失败后,可以编写重试逻辑(最多重试 3 次),避免业务失败;
  23. 事务中操作的 key 越多,执行效率越低,尽量精简事务内的命令;
  24. 不要在事务中执行大量命令,会导致事务队列过大,占用内存;
  25. 事务执行时间越短越好,避免长时间占用 Redis 资源;
  26. 事务执行结果是有序列表,和命令入队顺序完全一致,一一对应;
  27. 事务中删除 key、修改 key、新增 key,都不会影响事务队列;
  28. 事务模式下,客户端断开连接,事务自动放弃,队列自动清空;
  29. 事务不支持事务内的条件判断,所有命令只能按顺序执行;
  30. 零基础开发者,优先记住:Redis 事务 = 打包命令 + 一次性执行,无回滚!

第五节 Redis 事务适用 & 不适用场景

适合用 Redis 事务的场景

  1. 批量执行多个小命令,不希望被其他客户端命令打断、插队;
  2. 简单的并发控制,用WATCH防止轻微数据错乱、逻辑错误;
  3. 一次性提交多个用户修改(如修改昵称、头像、签名、个性介绍);
  4. 低并发、非核心业务,追求执行效率、简单操作;
  5. 测试环境、开发环境,快速批量执行命令,简化操作;
  6. 不需要严格原子性、不需要回滚的简单业务逻辑;
  7. 一次性写入多个缓存数据,保证写入连续性。

不适合用 Redis 事务的场景

  1. 金融转账、支付充值、提现、基金交易(需要严格回滚、原子性);
  2. 商品库存扣减、秒杀、抢购(防止超卖、少卖,需要强原子性);
  3. 高并发核心业务(WATCH频繁失败,用户体验极差);
  4. 需要数据合法性约束的业务(如余额不能为负、库存不能为负);
  5. 要求数据绝对一致、绝对安全、绝对严谨的核心业务;
  6. 多步骤、复杂逻辑、需要回滚的业务场景;
  7. 对数据准确性要求极高的金融、医疗、政务业务。

第六节 Redis 事务常见问题

1. 问:Redis 事务执行失败后,能不能手动回滚?

答:不能!绝对不能!Redis 没有任何回滚命令,也没有回滚功能!如果事务执行失败,只能自己手动编写补偿代码,把已经执行成功的命令手动撤销,Redis 完全不提供回滚能力。

2. 问:WATCH 监控后,自己修改监控的 key,事务会失败吗?

答:不会!只有其他客户端修改监控的 key,事务才会失败;当前客户端事务内的命令还没执行,不会改变 key 的版本号,所以不会触发监控失效。

3. 问:Redis 事务和 Lua 脚本,哪个更适合生产?

答:Lua 脚本更适合生产!Lua 脚本可以实现真正的原子性、复杂逻辑、伪回滚,解决 Redis 事务无回滚的缺陷,是生产中原子操作的首选。

4. 问:事务中可以执行 get 查询命令吗?

答:可以!get 命令会进入事务队列,执行后返回查询结果,和修改命令一样按顺序执行。

5. 问:Redis 事务会占用额外内存吗?

答:会!事务队列会暂存命令,命令越多、队列越大,占用的内存越多,所以事务内的命令要尽量精简。


第七节 总结

  1. Redis 事务 = 命令暂存入队 + 一次性连续执行 + 不被插队,这是核心本质;
  2. Redis 事务唯一保证:命令按顺序连续执行,不被其他客户端插队、打断、插入;
  3. Redis 事务没有回滚、没有原子性、没有一致性、没有隔离性、没有持久性,是弱化版事务;
  4. MULTI 开事务 → 命令入队显 QUEUED → EXEC 执行生效 / DISCARD 放弃清空 → WATCH 监控防并发 → UNWATCH 取消全监控;
  5. 语法拼写错误→整个事务全部作废,逻辑运行错误→单条失败其他执行;
  6. WATCH 核心原理:版本号对比,key 被改→事务失败,key 不变→事务正常;
  7. 严格安全、高并发、核心业务→绝对不用 Redis 事务,用 Lua 脚本;
  8. 事务和持久化完全无关,数据是否落盘看 AOF/RDB,和事务一毛钱关系没有;
  9. Redis 单线程 = 天然无并发冲突 = 不需要隔离级别 = 不需要复杂事务控制;
  10. Redis 事务就是打包命令一次性跑,错了不回滚,简单场景用,复杂场景别硬用!
  11. Redis 事务不是 MySQL 事务,不要用 MySQL 事务的思维理解 Redis 事务!
  12. Redis 事务的唯一价值就是保证命令连续执行,不被插队,仅此而已!

结语

hello 大家,到这里,Redis 事务从本质定义、核心命令、错误机制、生产避坑、场景选型全维度就全部讲透了,全程用最接地气的 ATM 取钱、快递站入队类比,把 Redis 这个「阉割版、极简版事务」的底层逻辑扒得明明白白,哪怕是零基础小白,也能彻底吃透、落地不踩坑。

回顾全篇,我们从一开始就打破了大家最容易陷入的误区:Redis 事务 ≠ MySQL 标准事务,这是学习 Redis 事务的第一前提,也是生产中不丢数据、不踩大坑的核心底线。它没有 MySQL 引以为傲的 ACID 四大特性,没有自动回滚,没有严格约束,没有复杂隔离级别,它的本质从头到尾都很简单:就是一个命令暂存队列,把一组操作打包,保证一次性、按顺序、不被插队地执行完,仅此而已。

我们再把整篇最核心、最需要刻在脑子里的知识点,浓缩成几句终极总结,方便你随时回顾、直接落地:

  1. 本质一句话:Redis 事务 = 命令入队暂存 + 一次性顺序执行 + 不被其他客户端打断,唯一保证就是「执行连续」,不保证「原子回滚」;
  2. 命令五件套:MULTI 开事务、命令入队显 QUEUED、EXEC 一键执行、DISCARD 清空放弃、WATCH 简易防并发、UNWATCH 取消监控;
  3. 错误铁律:语法拼写错 → 整个事务直接作废;逻辑运行错 → 单条命令失败,其他照常执行,无任何回滚;
  4. WATCH 定位:轻量级乐观锁,靠版本号防简单并发错乱,高并发场景频繁失效,别当分布式锁用;
  5. 生产终极选型:低并发非核心批量操作用事务,金融支付、秒杀库存、强一致性核心业务 → 直接用 Lua 脚本,别硬扛 Redis 事务。

其实 Redis 之所以设计出这么「简单甚至残缺」的事务,根本原因是它的定位:高性能内存数据库。标准事务的回滚、隔离、锁机制,都会严重拖慢 Redis 的执行效率,违背了它「快、轻、简」的核心设计理念。所以它只保留了事务最基础的「打包执行」能力,把更复杂的原子性、一致性需求,交给了 Lua 脚本去实现。

很多同学学完会觉得:Redis 事务这么弱,学它有什么用?恰恰相反,懂它的弱,才懂怎么用对它。知道它不能回滚,就不会在支付转账里乱用;知道它不支持强并发,就不会在秒杀场景硬上;知道它只是批量打包命令,就只会在简单批量操作里使用 —— 这才是学习 Redis 事务的真正价值:认清边界,不瞎用、不硬扛、不踩坑。

技术学习从来不是死记硬背知识点,而是理解设计初衷、掌握使用边界。本篇从理论到实操、从误区到实战、从命令到生产,把 Redis 事务讲得足够细、足够透,就是希望你在实际工作中,面对业务需求时,能快速做出正确选择,让 Redis 稳定、安全、高效地服务业务。

如果本篇保姆级教程对你有帮助,欢迎点赞、收藏、反复回看,也欢迎在评论区交流你在生产中遇到的 Redis 事务踩坑经历。后续我们还会继续拆解 Redis Lua 脚本原子实战、分布式锁、高并发秒杀方案,带你把 Redis 生产实战的每一个知识点都学透、落地到底。

一步一个脚印打牢基础,生产环境才不会掉链子。我们下篇博客见~

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2503_92929084/article/details/157946729

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--