Redis 支持事务机制,但其事务模型与 MySQL等传统关系型数据库的 ACID 事务存在本质差异,Redis实现事务机制的核心是通过 "命令批量执行 + 非原子性 + 乐观锁" 的方式实现并发场景下的命令组执行控制。
Redis 事务并非严格意义上的 "原子性事务",其核心目标是保证一个事务中的所有命令被序列化、按顺序执行,在事务执行期间,不会被其他客户端发送的命令插入,但不保证 "全部成功或全部失败" 的原子性。在传统数据库事务中,事务通常遵循 ACID 原则,即原子性、一致性、隔离性、持久性,事务执行失败会触发回滚;而在Redis 事务中,其仅保证 "序列化执行" 和 "隔离性"(这是因为单线程执行特性天然保证了隔离性),且不支持回滚机制,即使事务中某条命令执行失败,后续命令仍会继续执行,也不严格保证持久性(严格程度取决于持久化策略)。
edis 事务通过 MULTI、EXEC、DISCARD、WATCH 四个核心命令完成生命周期管理,整体流程分为 "开启事务→入队命令→执行 / 放弃事务" 三步:
(1)开启事务(MULTI):调用 MULTI 命令后,客户端进入 "事务模式",此时 Redis 不会立即执行后续命令,而是将所有命令逐个加入事务队列;
(2)命令入队:事务模式下,客户端发送的所有常规命令(如 SET、GET、HSET 等)不会立即执行,Redis 会返回QUEUED表示命令已入队,若命令语法错误(如参数数量不对),Redis 会立即返回错误,但不终止事务;
(3)执行 / 放弃事务:EXEC命令触发事务执行,Redis 按入队顺序依次执行所有命令,返回所有命令的执行结果列表;而DISCARD命令则是放弃事务,清空事务队列,退出事务模式,不会执行任何入队命令。
示例流程:
- MULTI (开启事务)
2.SET key1 value1 (命令入队)
3.INCR key2 (命令入队)
4.EXEC/DISCARD (执行事务/放弃事务)
与 MySQL等传统关系型数据库不同,Redis 事务本身不支持悲观锁,其主要通过 WATCH 命令实现乐观锁,用于解决事务执行期间的并发数据修改问题。而WATCH 作用主要是监控一个或多个 key,若在调用 EXEC 执行事务前,被监控的 key 被其他客户端修改,整个事务会被取消执行,EXEC 返回nil。其原理主要是,WATCH 为监控的 key 关联 一个"版本号",事务执行时 Redis 会检查 key 的版本号,若与监控时不一致,则判定为数据已被修改,放弃事务。同时,若事务因 WATCH 触发失败,客户端需重新执行 WATCH + MULTI + 命令入队 + EXEC 流程,实现 "检测 - 重试" 的乐观锁策略。
Redis 事务失败分为 "入队时失败" 和 "执行时失败",二者处理逻辑差异显著,也是其 "非原子性" 的核心体现:
(1)入队时失败(语法错误):如命令参数错误、命令不存在,Redis 会立即返回错误信息,此时调用 EXEC 会直接放弃整个事务,无任何命令被执行;
(2)执行时失败(逻辑错误):如对字符串执行 INCR 操作、操作不存在的哈希字段,Redis 仅返回该命令的错误结果,后续命令仍会继续执行,且已执行的命令不会回滚。
Redis的事务主要适用于以下几个场景:
(1)批量执行多个命令,确保命令按顺序执行且不被其他客户端打断(如批量更新缓存、一次性设置多个关联 key);
(2)结合 WATCH 实现简单的并发控制(如库存扣减、余额转账等低并发场景的乐观锁控制);
(3)无需严格原子性、可接受 "部分成功" 的场景(如日志批量写入、非核心数据的批量更新)。
而在Redis 6.0+之后,其获得了一些增强,主要有CLUSTEREXEC 与事务扩展,其主要是针对集群模式,为其优化了事务执行,相较于传统 的EXEC 指令,在集群模式下,若事务中的命令涉及多个哈希槽,EXEC 指令会直接失败。而CLUSTEREXEC 支持跨哈希槽的事务执行,通过 "事务分片 + 批量提交",可以实现在集群模式下的命令批量执行保持 Redis 事务的非原子性特性。