Redis事务

Redis事务

引言

我们通常理解的"事务",简单来说就是一句话:要么一起成功,要么一起失败,绝不能出现"只完成一半"的情况。

这就好比一场资金转账。假设用户A要给用户B转账5块钱。在程序里,这通常包含两个动作:

  1. 扣款:从用户A的账户里扣除5块钱。
  2. 入账:给用户B的账户增加5块钱。

在现实世界里,这两个动作必须是铁板一块的。如果扣款成功了,但入账失败了(比如网络突然断了),那这5块钱就凭空消失了,这在金融系统里是绝对无法容忍的灾难。反之,如果扣款失败了,入账却成功了,那银行就亏大了。 所以,我们需要一种机制,把这两个动作打包成一个"不可分割"的整体。这就是原子性(Atomicity)。

Redis执行事务的命令

第一步:WATCH

行动开始前,我们需要确保目标数据是"安全"的。WATCH 命令就是我们的"盯梢员"。

在执行事务之前,我们先用 WATCH key [key ...] 监视那些关键的键(比如用户的余额)。这就好比在转账前,你死死盯着账户余额的数字。如果在行动结束前,这个数字被其他人改动了,那我们的行动就会立刻中止,以此来避免基于过期数据做出错误的决策。

第二步:MULTI

一旦确认环境"安全",我们就可以开始收集指令了。MULTI 命令标志着事务的正式开始。

当你输入 MULTI 后,Redis会进入"排队模式"。接下来你输入的所有命令(比如扣款、入账),都不会立即执行,而是被放入一个队列中静静等待。这一步确保了我们的操作序列是完整的,不会被中间插队。

第三步:EXEC

当所有命令都准备就绪,就是发起总攻的时刻。EXEC 命令会触发队列中所有命令的执行。

Redis会一口气、按顺序地把队列里的命令全部执行完。更重要的是,它会先回头检查一下 WATCH 盯着的那些键:如果它们没变,命令就顺利执行;如果它们变了,Redis会直接放弃整个队列的操作(返回nil),绝不允许出现"半成品"的脏数据。

补充动作:解散与重置

  • DISCARD:如果你在 MULTI 之后突然不想干了,可以用 DISCARD 来清空队列,让Redis恢复到正常状态。
  • UNWATCH:如果你不想再监视某些键了,或者事务执行完毕(EXECDISCARD 后会自动取消监视),可以用 UNWATCH 来取消监视,释放资源。

"伪"原子性

在 MySQL 等传统关系型数据库中,原子性意味着"全有或全无"(All or Nothing)。如果中间出错,系统会自动执行回滚(Rollback),把数据恢复到最初的状态。

然而,Redis 事务的哲学截然不同。它奉行"简单至上 ",不支持传统意义上的回滚机制。这主要体现在两个方面:

  1. 不回滚业务错误

    如果事务中的某条命令执行失败(比如对一个字符串类型的键执行 INCR),Redis 不会撤销之前已经执行成功的命令。例如,A扣款成功了,但给B入账时命令写错了,Redis不会把A的钱还回去。它只会记录这个错误并继续执行后续命令(如果有的话)。

  2. 靠"放弃"来保证一致性

    既然不支持回滚,那怎么保证数据不出错呢?答案就是 WATCH。

    Redis 的策略是:与其出错后费力地回滚,不如在出错前直接放弃。

    通过 WATCH 机制,Redis 能够检测到数据是否被并发修改。如果检测到冲突,它不会去尝试修正数据,而是直接放弃整个事务(返回 nil)。这就把处理问题的责任推给了客户端------客户端收到失败信号后,应该重新尝试整个操作。

为什么没有回滚?

你可能会问,为什么不实现像MySQL那样的回滚机制呢?官方的解释是:Redis追求简单和极致的性能。回滚机制需要记录undo log(回滚日志),这会带来额外的开销和复杂性。对于Redis来说,大多数命令都是简单的写操作,且数据通常作为缓存存在,即使出错,也可以通过从后端数据库重新加载来恢复。因此,Redis选择了一种更轻量级的方案。

与Lua脚本的抉择

Redis还支持通过EVAL命令执行Lua脚本。Lua脚本在Redis中也是原子执行的,并且它支持真正的条件判断和错误处理。在很多场景下,Lua脚本可以替代Redis事务,甚至比事务更强大、更简洁。

那么,何时使用事务,何时使用Lua脚本呢?

  • 使用Redis事务:当你需要执行的是一组简单的、固定的Redis命令,且业务逻辑相对简单时。
  • 使用Lua脚本:当你需要复杂的逻辑判断、循环、或者需要在服务端进行复杂的计算时。Lua脚本将逻辑封装在服务端,减少了网络往返,性能通常更好。
相关推荐
数据知道1 小时前
PostgreSQL:如何定期验证备份的有效性?(灾备演练)
数据库·postgresql
档案宝档案管理1 小时前
档案管理系统软件:档案宝让企业实现高效档案利用与精准数据分析
大数据·数据库·人工智能·档案·档案管理
eWidget1 小时前
核心业务系统“去O”实战:如何破解语法兼容与逻辑重构难题?核心业务系统“去O”实战:如何破解语法兼容与逻辑重构难题?
数据库·oracle·重构·kingbase·数据库平替用金仓·金仓数据库
2501_941982051 小时前
Python开发:外部群消息自动回复
java·前端·数据库
qinaoaini1 小时前
Spring中Aware的用法以及实现
java·数据库·spring
BingoGo2 小时前
如何重构遗留 PHP 代码 不至于崩溃
后端·php
OceanBase数据库官方博客2 小时前
从分库分表到原生分布式:高德基于 OceanBase 的数据底座演进之路
数据库·oceanbase·分布式数据库
探序基因2 小时前
knockTF2.0数据库-找上游转录因子
数据库·经验分享·笔记
Web极客码2 小时前
修复Discuz 迁移后页面全部变成“????”乱码的问题
数据库·mysql·discuz·mariadb