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

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

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

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