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

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

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

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