Redis 事务

发布时间:2026/5/17 2:23:33

Redis 事务 Redis 事务从命令队列到 WATCH 乐观锁在学习 Redis 的时候很多人会自然地把 Redis 事务和 MySQL 事务放在一起理解。它们确实有相似的地方都是把一组操作组织起来作为一个整体批量提交。但 Redis 的事务能力要轻很多它更像是“命令打包执行机制”而不是传统数据库里完整的 ACID 事务。这篇文章按照第五章的内容顺序梳理 Redis 事务的核心概念、常用命令以及WATCH是如何解决并发修改风险的。一、什么是 Redis 事务Redis 事务可以把多个命令放到同一个执行单元中。客户端先开启事务然后把要执行的命令逐条加入队列最后通过EXEC一次性提交。从使用感受上看它和 MySQL 事务都能把一批操作绑定在一起但从语义上看Redis 事务和 MySQL 事务差异很大。1. Redis 的原子性是弱化的MySQL 事务强调“要么都成功要么都失败”失败时可以回滚到事务开始前的状态。Redis 没有这样的回滚机制。Redis 事务主要保证命令会被批量、连续地执行但如果执行过程中某条命令因为运行时错误失败Redis 不会自动把前面已经执行成功的命令撤销。所以Redis 事务不能简单理解成 MySQL 那种强事务。2. Redis 不负责传统意义上的一致性约束MySQL 中的一致性往往和表结构、约束、外键、唯一索引等机制有关。事务提交前后数据库都应该处于合法状态。Redis 本身是键值数据库不提供类似关系型数据库的复杂约束体系也没有回滚机制。因此Redis 事务并不负责帮我们维护这种强一致性更多一致性逻辑需要业务自己设计。3. Redis 不需要复杂的隔离级别MySQL 有读未提交、读已提交、可重复读、串行化等隔离级别是因为多个事务可能并发执行。Redis 的命令执行模型是单线程的同一时刻只会执行一个命令。事务一旦进入真正执行阶段队列中的命令会连续执行中间不会被其他客户端的命令插入。因此 Redis 不需要像 MySQL 那样设计复杂的隔离级别。4. Redis 事务不直接保证持久性Redis 的数据主要在内存中。是否持久化取决于 Redis 服务端是否开启了 RDB、AOF 等持久化机制。也就是说Redis 事务本身不负责持久性。事务提交成功只表示命令在 Redis 内存数据中执行完成至于是否落盘是持久化配置负责的事情。二、Redis 事务的本质事务队列Redis 事务的核心可以理解为一个“事务队列”。当客户端执行MULTI后Redis 会进入事务状态。之后客户端发送的命令不会立即执行而是被放进队列中。只有当客户端发送EXEC时Redis 才会把队列里的命令按顺序执行。因此Redis 事务最重要的保证是事务中的命令会按入队顺序执行执行事务队列时中间不会被其他客户端命令插入事务没有传统数据库那样的自动回滚能力。理解这一点后面的几个命令就很好掌握了。三、事务相关命令Redis 事务常用命令主要有五个MULTI、EXEC、DISCARD、WATCH、UNWATCH。1. MULTI开启事务MULTI用来开启一个事务。执行成功后Redis 会返回OK表示当前客户端进入事务状态。127.0.0.1:6379 MULTI OK开启事务后后续命令会进入事务队列而不是立即执行。2. EXEC提交并执行事务EXEC用来提交事务。Redis 收到EXEC后会按顺序执行事务队列中的所有命令。例如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 EXEC 1) OK 2) OK 3) OK这里每次执行SET时Redis 返回的是QUEUED表示命令已经进入队列。只有执行EXEC后k1、k2、k3才真正写入 Redis。执行完成后再查询127.0.0.1:6379 GET k1 1 127.0.0.1:6379 GET k2 2 127.0.0.1:6379 GET k3 3这就是 Redis 事务的基本执行流程先排队再提交。3. DISCARD放弃事务如果开启事务后不想提交可以使用DISCARD放弃当前事务。它会清空事务队列之前排队的命令不会执行。例如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 DISCARD OK此时再查询127.0.0.1:6379 GET k1 (nil) 127.0.0.1:6379 GET k2 (nil)可以看到虽然SET k1 1和SET k2 2曾经进入过队列但由于事务被放弃它们没有真正执行。4. WATCH监控 key避免并发修改风险Redis 事务虽然能保证命令连续执行但在事务真正提交之前其他客户端仍然可能修改相关 key。这会带来一个典型问题事务里排队的命令可能基于已经过期的判断。看一个场景。客户端 1 开启事务并准备修改key127.0.0.1:6379 MULTI OK 127.0.0.1:6379 SET key 100 QUEUED在客户端 1 还没有执行EXEC时客户端 2 修改了同一个 key127.0.0.1:6379 SET key 200 OK随后客户端 1 提交事务127.0.0.1:6379 EXEC 1) OK最终key的值会变成100。从命令输入顺序看好像客户端 1 先设置客户端 2 后设置但从真正执行顺序看是客户端 2 先执行成功客户端 1 的事务最后提交成功。这种结果很容易让业务产生误判。为了解决这类问题Redis 提供了WATCH。WATCH可以在事务提交前监控一个或多个 key。被监控的 key 如果在事务提交前被其他客户端修改那么当前事务执行EXEC时会失败队列里的命令都不会执行。它的思想类似乐观锁先记录被监控 key 的版本提交时再检查版本是否变化。如果变化了说明期间有人改过数据这次事务就不再提交。示例流程如下。客户端 1 先监控k1再开启事务127.0.0.1:6379 WATCH k1 OK 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此时客户端 1 只是把命令放入队列还没有提交。客户端 2 在中途修改k1127.0.0.1:6379 SET k1 200 OK客户端 1 再提交事务127.0.0.1:6379 EXEC (nil)EXEC返回空表示事务没有执行。再查询数据127.0.0.1:6379 GET k1 200 127.0.0.1:6379 GET k2 (nil)可以看到k1保留了客户端 2 写入的值k2也没有被客户端 1 的事务写入。这说明事务被取消队列中的命令都没有执行。5. UNWATCH取消监控UNWATCH用来取消当前客户端对 key 的监控。如果执行了WATCH但后来不打算继续基于这些 key 提交事务可以使用UNWATCH主动解除监控状态。127.0.0.1:6379 UNWATCH OK它可以理解为WATCH的反向操作。四、总结Redis 事务的重点不是“强 ACID”而是“命令队列 连续执行”。它适合用来把一组 Redis 命令按顺序打包提交但不能期待它像 MySQL 一样提供完整的回滚、约束、一致性和持久性保证。掌握 Redis 事务时可以记住这几句话MULTI开启事务普通命令进入队列返回QUEUEDEXEC提交队列并执行DISCARD放弃队列WATCH监控 key发现提交前被改动就取消事务UNWATCH取消监控。如果业务对并发修改比较敏感单纯使用MULTI和EXEC不够还应该结合WATCH做乐观锁控制。这样才能在 Redis 轻量事务模型下尽量避免数据被意外覆盖。

相关新闻