Redis事务与悲观锁乐观锁实现详解

作者:技术拾光

时间:2026-05-21

浏览:613

Redis 事务详解 Redis 事务与常见的关系型数据库(如 MySQL)事务存在显著差异。其核心机制是将多个命令打包,然后一次性、按顺序执行。Redis 事务的执行过程遵循以下三个关键原则: 在 EXEC 命令执行前,所有命令仅被缓存至队列,不会立即执行。 执行 EXEC 后,队列中的命令按序执

文章目录

Redis 事务详解

Redis 事务与常见的关系型数据库(如 MySQL)事务存在显著差异。其核心机制是将多个命令打包,然后一次性、按顺序执行。Redis 事务的执行过程遵循以下三个关键原则:

  • 在 EXEC 命令执行前,所有命令仅被缓存至队列,不会立即执行。
  • 执行 EXEC 后,队列中的命令按序执行。即使某个命令执行出错,也不会影响队列中其他命令的执行。
  • 事务执行期间,其他客户端提交的命令请求无法插入当前事务队列,确保了事务序列的独立性。

Redis 事务的执行流程可分为三个阶段:

  • 开启事务(MULTI):使用 MULTI 命令标记事务开始。
  • 命令入队(Queue):后续输入的命令进入队列等待执行。
  • 执行事务(EXEC):输入 EXEC 命令,Redis 按序执行队列中的所有命令。

这引出了 Redis 事务与 MySQL 事务的一个核心区别:原子性。

  • Redis 事务不具备传统意义上的原子性。若事务队列中某条命令执行失败(例如对错误数据类型进行操作),该命令会报错,但事务会继续执行剩余命令,不会整体回滚。
  • Redis 的每条单独命令具备原子性,这是其设计基础。
  • MySQL 事务严格遵循 ACID 原则,具备原子性。事务中任何 SQL 语句失败,都会导致所有操作回滚,数据库状态恢复至事务开始前。

以下图示展示了 Redis 事务的基本操作流程:

Redis事务与悲观锁乐观锁实现详解

Redis 事务中的错误主要分为两类:

1. 编译时异常:指命令入队时语法错误,例如输入了不存在的命令。此时,整个事务中的所有命令均不会执行。如下图所示:

Redis事务与悲观锁乐观锁实现详解

2. 运行时异常:指命令语法正确且入队成功,但执行时出错,例如对字符串类型值执行列表的 LPOP 操作。此时,仅出错命令失败,事务中的其他命令正常执行。如下图所示:

Redis事务与悲观锁乐观锁实现详解

事务的 ACID 原则分析

ACID 是衡量事务的四个核心特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。

一、原子性(Atomicity)

原子性要求事务是一个不可分割的工作单元,其中的操作要么全部成功,要么全部失败回滚,不存在中间状态。

二、一致性(Consistency)

一致性确保事务必须使数据库从一个一致状态转换到另一个一致状态,维护数据的完整性和业务规则约束。若事务中途失败导致部分修改写入,数据库将处于不一致的错误状态。

三、隔离性(Isolation)

隔离性定义了并发事务间的关系。在并发环境中,每个事务都感觉不到其他事务同时执行,彼此隔离。这意味着一个事务内部的操作和数据对其他并发事务不可见。

SQL 标准定义了四个隔离级别,级别越高隔离性越强,但并发性能通常下降:

1. 读未提交(Read Uncommitted):最低隔离级别,允许“脏读”。事务 A 可读取事务 B 尚未提交的中间修改值。

2. 读已提交(Read Committed):只允许读取已提交的数据,解决了脏读问题,但可能导致“不可重复读”——同一事务中两次读取同一数据可能结果不同。

3. 可重复读(Repeatable Read):保证同一事务中多次读取同一数据结果一致,解决了不可重复读问题,但可能出现“幻读”,即前后两次范围查询的结果集数量不一致。

4. 串行化(Serializable):最严格的隔离级别,强制所有事务串行执行,完全避免脏读、不可重复读和幻读,但并发性能最低。

四、持久性(Durability)

持久性意味着事务成功提交后,其对数据库的改变是永久性的,即使系统崩溃或重启,这些改变也不会丢失。

Redis 实现悲观锁与乐观锁

在并发控制中,悲观锁和乐观锁是两种经典策略,Redis 可以有效地实现它们。

1. 悲观锁与乐观锁的概念

  • 悲观锁:持“悲观”态度,认为数据在操作过程中很可能被他人修改。因此,在对数据进行任何操作前先加锁,阻止其他线程访问,直至操作完成释放锁。传统关系型数据库的行锁、表锁即属此类,保证了数据安全但可能牺牲并发效率。
  • 乐观锁:持“乐观”态度,认为数据冲突概率较低。读取数据时不加锁,更新时通过版本号机制判断数据是否被修改。读取时记录版本号,更新时检查当前版本号是否与之前一致,一致则更新成功并递增版本号,否则更新失败需重试。

Redis事务与悲观锁乐观锁实现详解

2. 乐观锁与悲观锁的应用场景

  • 乐观锁适用于读多写少的场景。读取操作不上锁,性能高;但写操作频繁时,可能因版本冲突导致大量更新失败和重试。
  • 悲观锁适用于写多读少的场景。提前加锁保证了写入时的数据安全,避免了重试开销;但读操作频繁时,加锁可能导致性能瓶颈。

3. Redis 如何实现乐观锁

Redis 通过 WATCH 命令实现乐观锁机制。可对一个或多个 key 进行“监视”(WATCH)。若在事务执行(EXEC)前,这些被监视的 key 被其他客户端修改,则当前客户端的事务将执行失败。

  • 具体流程:客户端 A 使用 WATCH 监控一个 key,开启事务(MULTI)并准备操作。期间若客户端 B 修改了该 key 的值,则客户端 A 执行 EXEC 时,事务会被丢弃(DISCARD),所有命令均不执行。
  • 注意:WATCH 机制仅对事务有效。若不开启事务直接执行命令则无影响,因为 Redis 的单条命令具备原子性。同样,开启新事务(重新 WATCH)操作该 key 也是允许的。

以下为客户端 A 与客户端 B 交互示例:

客户端 A 操作序列:

Redis事务与悲观锁乐观锁实现详解

客户端 B 在 A 的事务执行期间修改了被监控的 key:

Redis事务与悲观锁乐观锁实现详解

一旦被 WATCH 的 key 在事务执行前被改动,当前客户端的事务操作将全部失败,体现了乐观锁的“检测-冲突”处理模式。

总结

Redis 事务提供了命令打包执行的机制,但其原子性保证不同于关系型数据库,更侧重于命令序列的连续执行而非整体回滚。结合 WATCH 命令,Redis 能够实现高效的乐观锁,非常适用于并发读多写少场景下的数据一致性控制。理解这些特性有助于在实际开发中选择合适的并发控制方案。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章