• Redis 事务那些事儿:实用技巧和避坑指南!

Redis 事务那些事儿:实用技巧和避坑指南!

2025-06-18 10:00:07 栏目:宝塔面板 4 阅读

一、为什么我们关心Redis事务?

在Java开发的日常工作中,Redis几乎无处不在。你可能用它做缓存、排行榜、分布式锁,甚至用它做轻量级的数据存储。

但随着业务复杂度提升,很多人都会遇到这样的问题:

  • 多个Redis操作需要保证原子性,怎么做?
  • Redis的事务和MySQL事务一样靠谱吗?
  • WATCH、MULTI、EXEC这些命令到底怎么用?能不能防止并发下的数据不一致?

这些问题看似简单,实则暗藏不少坑。今天这篇文章,我想用最实在的语言,把Redis事务的本质、用法和注意事项讲清楚,帮你在实际开发中少踩坑。

二、Redis事务机制全解析

1. Redis到底支不支持事务?

结论先行:Redis支持事务,但和MySQL事务完全不是一回事。

MySQL事务强调ACID(原子性、一致性、隔离性、持久性),而Redis的事务机制更像是“命令打包、顺序执行”,没有复杂的隔离和回滚机制。

2. Redis事务的基本命令和用法

Redis事务的核心命令有四个:MULTI、EXEC、DISCARD、WATCH。

(1) MULTI/EXEC:事务的开始与提交

  • MULTI:开启事务,后续命令进入队列
  • EXEC:提交事务,队列中的命令依次执行

举个例子:

# 1. 初始化库存
127.0.0.1:6379> set a:stock 100
OK
127.0.0.1:6379> set b:stock 200
OK
# 2. 开启事务
127.0.0.1:6379> multi
OK
# 3. 将a:stock减1
127.0.0.1:6379> decr a:stock
QUEUED
# 4. 将b:stock减1
127.0.0.1:6379> decr b:stock
QUEUED
# 5. 实际执行事务
127.0.0.1:6379> exec
1) (integer) 99
2) (integer) 199
127.0.0.1:6379>

(2) DISCARD:放弃事务

如果在MULTI之后,发现有问题,可以用DISCARD放弃事务,清空命令队列。

(3) WATCH:乐观锁的实现

在并发场景下,单靠MULTI/EXEC还不够。比如转账操作,两个客户端同时读取余额,都判断可以转账,结果都扣了钱,余额就出错了。

这时候可以用WATCH命令,类似乐观锁。WATCH会监控指定的key,如果在事务提交前这些key被其他客户端修改,EXEC会失败,事务不会执行。

示例:

127.0.0.1:6379> get a:stock
"99"
127.0.0.1:6379> watch a:stock
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decr a:stock
QUEUED
127.0.0.1:6379> decr b:stock
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379>

如上所示,a:stock在EXEC前被其他客户端修改,EXEC会返回null,表示事务失败。

三、Redis事务的常见“坑”和注意事项

1. 没有回滚机制

只要EXEC执行,前面的命令就算后面有错,也不会回滚。比如:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name tom
QUEUED
127.0.0.1:6379> incr name 
QUEUED
127.0.0.1:6379> set age 18
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379>
  • set name tom执行成功。
  • incr name 执行时报错(因为 name 是字符串,不能自增)。
  • set age 18依然会被执行。

注意:Redis事务中,某条命令出错不会影响其他命令的执行,也不会回滚。

那如果我们想实现回滚的效果怎么办呢?

2. 如何用DISCARD修复?

如果你在MULTI之后发现命令写错了,可以在EXEC之前执行DISCARD,这样所有已入队的命令都不会被执行,数据不会被修改。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set addr bj
QUEUED
127.0.0.1:6379> incr addr
QUEUED
127.0.0.1:6379> set code 110
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get addr
(nil)
127.0.0.1:6379>

执行结果:

  • set addr bj、set code 110都不会被执行。
  • 事务被彻底放弃,Redis状态不会有任何变化。

注意点:

  • 一旦执行了EXEC,就无法再用DISCARD撤销事务,已经执行的命令不会回滚。
  • DISCARD只能在事务提交前使用,相当于“撤销”本次事务。

3. 没有隔离性

Redis事务期间,其他客户端依然可以操作相关key。WATCH只能监控key本身的变化,不能保证更复杂的业务一致性。

比如你WATCH了a:stock,但b:stock被其他客户端修改了,你的事务依然会执行。

四、实用干货:Redis事务的正确打开方式

(1) 能用原子命令就用原子命令

Redis本身很多命令就是原子的,比如INCR、DECR、SETNX等,优先用这些。

(2) 事务只保证命令的“批量、顺序、一次性”执行

不保证命令之间的隔离和回滚。

(3) WATCH适合乐观锁场景

比如扣库存、转账等,先WATCH关键key,判断条件后再MULTI/EXEC。

(4) 复杂业务建议用Lua脚本

Lua脚本在Redis中是原子执行的,可以实现更复杂的业务逻辑和回滚。

(5) 不要把Redis事务当成数据库事务用

Redis事务和MySQL事务完全不是一回事,不能指望它帮你兜底所有一致性问题。

五、Redis事务和ACID的对比

很多同学会问:Redis事务到底支持ACID的哪几项?

(1) 原子性(Atomicity)

Redis事务只保证“命令队列”整体的原子性,不保证单条命令的原子性。EXEC时,要么所有命令都执行,要么都不执行(WATCH监控失败时)。

(2) 一致性(Consistency)

Redis事务本身不保证数据的一致性,需要开发者自己保证。

(3) 隔离性(Isolation)

Redis事务没有严格的隔离性,事务执行期间,其他客户端可以修改相关key。

(4) 持久性(Durability)

取决于Redis的持久化配置(RDB、AOF),和事务机制本身无关。

一句话总结:Redis事务只保证“命令批量执行的原子性”,不保证隔离和回滚。

六、面试高频问答

(1) Redis事务和MySQL事务的区别?

  • MySQL事务支持ACID,Redis事务只保证命令批量执行的原子性。
  • MySQL事务有回滚机制,Redis事务没有。
  • MySQL事务有隔离级别,Redis事务没有。

(2) Redis事务失败会回滚吗?

不会。只要EXEC执行,前面的命令就算后面有错,也不会回滚。

(3) WATCH命令的作用是什么?

实现乐观锁,监控指定key,防止并发下的数据不一致。

(4) Redis事务适合哪些场景?

适合批量命令、简单乐观锁场景。不适合强一致性、复杂回滚的业务。

本文地址:https://www.yitenyun.com/294.html

搜索文章

Tags

数据库 API FastAPI Calcite 电商系统 MySQL 数据同步 ACK Web 应用 异步数据库 双主架构 循环复制 Deepseek 宝塔面板 Linux宝塔 Docker JumpServer JumpServer安装 堡垒机安装 Linux安装JumpServer 序列 核心机制 生命周期 esxi esxi6 root密码不对 无法登录 web无法登录 Windows Windows server net3.5 .NET 安装出错 宝塔面板打不开 宝塔面板无法访问 SSL 堡垒机 跳板机 HTTPS Windows宝塔 Mysql重置密码 无法访问宝塔面板 HTTPS加密 查看硬件 Linux查看硬件 Linux查看CPU Linux查看内存 连接控制 机制 ES 协同 scp Linux的scp怎么用 scp上传 scp下载 scp命令 Serverless 无服务器 语言 修改DNS Centos7如何修改DNS 日志文件 MIXED 3 Oracle 处理机制 RocketMQ 长轮询 配置 防火墙 服务器 黑客 Spring SQL 动态查询 存储 Rsync HexHub MySQL 9.3 缓存方案 缓存架构 缓存穿透 Linux 安全 加密 场景 Canal 开源 PostgreSQL 存储引擎 网络架构 工具 网络配置 聚簇 非聚簇 索引 Redis 自定义序列化 B+Tree ID 字段 线上 库存 预扣 分页查询 数据 业务 响应模型 InnoDB 数据库锁 信息化 智能运维 架构 DBMS 管理系统 Redis 8.0 单点故障 ​Redis 机器学习 推荐模型 分库 分表 共享锁 AI 助手 prometheus Alert GreatSQL Hash 字段 Netstat Linux 服务器 端口 openHalo 查询 监控 SQLark 优化 万能公式 云原生 Doris SeaTunnel 自动重启 运维 容器化 电商 系统 SVM Embedding OB 单机版 技术 排行榜 排序 不宕机 Postgres OTel Iceberg sftp 服务器 参数 虚拟服务器 虚拟机 内存 SpringAI 崖山 新版本 高可用 OAuth2 Token RDB AOF 人工智能 向量数据库 推荐系统 大模型 缓存 sqlmock PostGIS • 索引 • 数据库 Entity 开发 数据集成工具 IT运维 redo log 重做日志 SQLite-Web SQLite 数据库管理工具 Python Web MongoDB 容器 ZODB Testcloud 云端自动化 MVCC 分页 数据结构 EasyExcel MySQL8 Redka 同城 双活 数据备份 聚簇索引 非聚簇索引 LRU 向量库 Milvus StarRocks 数据仓库 微软 SQL Server AI功能 分布式架构 分布式锁​ 数据类型 PG DBA Ftp IT AIOPS 流量 数据脱敏 加密算法 池化技术 连接池 部署 1 mini-redis INCR指令 磁盘架构 MCP 开放协议 Web 接口 R2DBC 悲观锁 乐观锁 Caffeine CP 窗口 函数 字典 RAG HelixDB 原子性 单线程 线程 dbt 数据转换工具 双引擎 QPS 高并发 模型 引擎 性能 速度 服务器中毒 事务隔离 连接数 网络故障 主库 对象 Undo Log 网络 Pottery InfluxDB JOIN 工具链 Order SSH LLM Redisson 锁芯 频繁 Codis 高效统计 今天这篇文章就跟大家 Crash 代码 INSERT COMPACT 优化器 List 类型 Go 数据库迁移 Recursive filelock 大表 业务场景 发件箱模式 事务同步 分布式 集中式 分页方案 排版 传统数据库 向量化 管理口 意向锁 记录锁 线程安全 仪表盘 订单 UUIDv7 主键 启动故障 核心架构 订阅机制 Pump 读写 数据分类 事务 Java