Redis WATCH 怎样在并发修改时放弃事务
Redis 的 WATCH 让 EXEC 带上条件:从 WATCH 建立监视到 EXEC 检查时,只要任一被监视键发生了会使监视失效的修改,EXEC 就返回空回复,整组排队命令都不执行。这不是执行失败后回滚,而是在执行前放弃本次事务。
如果我要根据读到的旧配置计算一个新配置,我会先考虑 WATCH:竞争较少时,允许别人先修改,自己发现冲突后重读即可。但如果只是计数加一,我会直接选 INCR,而不是为了“用了事务”增加往返与重试。本文比较的是这类取舍,不声称发生过某次实际线上事故。
官方地址:https://redis.io/docs/latest/commands/watch/。以下双连接示例供读者在隔离测试实例操作,本文未执行命令,结果描述均为预期。
我会先确认是冲突,还是命令错误
日志里出现“事务没成功”时,我不会立即增加重试次数,而会先分清 EXEC 的返回类型:
| 观察到的结果 | 含义 | 处理方向 |
|---|---|---|
| EXEC 空回复 | WATCH 条件失效,本次队列未执行 | 重新读取并计算,按预算重试 |
| EXEC 返回数组,某个元素是错误 | 进入执行阶段,某条命令报错 | 逐项处理,不能假定其他写入撤销 |
| EXECABORT 等命令级错误 | 例如入队阶段错误导致事务被拒绝 | 修正命令或环境,不当普通竞争重试 |
| 超时或连接断开,没有确定回复 | 客户端不知道最终结果 | 不能直接归类为 WATCH 冲突 |
空回复也不等于“成功执行了零条命令”的空数组。客户端可能把冲突表示为专门异常或状态,应用应遵循所用驱动的返回约定,而非用列表是否为空或日志是否有文字判断。
用两个持久连接看清中止条件
读者需要两个长期保持的客户端连接 A、B,连接到同一测试实例和数据库。在两个现有命令会话中分别进入 redis-cli:
# 在两个独立的命令会话中各执行一次,后续命令在各自 redis-cli 内输入 # 仅连接隔离测试实例;不要在业务实例上初始化示例键 redis-cli
确认专用样例键可用于测试后,按下表人工交错输入。表中“暂停”是操作提示,不是 Redis 命令;不要把两列一次粘贴到同一连接。
| 操作位置 | 输入或动作 | 预期观察 |
|---|---|---|
| B:准备数据 | SET article:watch:config v1 | 设置已知初值 |
| A:建立监视 | WATCH article:watch:config | OK,只表示建立监视 |
| A:读取输入 | GET article:watch:config,随后暂停 | 读到 v1 |
| B:并发改写 | SET article:watch:config v2 | B 可以正常写入,不被 WATCH 阻塞 |
| A:准备事务 | MULTI,再输入 SET article:watch:config v3 | 分别回复 OK、QUEUED |
| A:提交 | EXEC | 空回复,不会执行这次 SET |
| A:教学观察 | GET article:watch:config | 没有其他写入时仍为 v2 |
关键不在于 v1、v2 是否不同,而在于监视期间是否发生修改。WATCH 不是在最后把两个字符串做一次相等比较。QUEUED 也只表示命令已入队,不能当成更新成功。
这个结构中,连接 A 持有监视状态和事务队列,二者都关联同一个被监视键;连接 B 的写命令是外部修改来源,EXEC 是条件提交的入口。图中的连线只表达这些归属和依赖。

连接边界比重试次数更重要
我会先检查 WATCH、读取、MULTI、EXEC 是否由同一条连接完成。逐次运行独立的 redis-cli WATCH ...、redis-cli GET ... 并不能拼成一次事务:前一条进程退出,监视连接也结束了。
应用连接池同样需要显式事务会话或连接绑定,不能把这几条调用随意分配给不同连接,也不能让其他请求共享处于监视或事务状态的连接。若更新依赖多个键,应在读取这些依赖之前监视所有相关键;只监视最终写入键,不能保护遗漏的读取条件。
不继续操作且尚未进入 MULTI 时,用 UNWATCH 清除监视;已经进入 MULTI 而决定放弃,用 DISCARD 清空队列并结束事务,同时取消监视。不要在排队后用 UNWATCH 充当事务取消命令。EXEC 无论成功还是因冲突放弃,都会清除监视;连接关闭也会清除。
连接归还池之前,应由驱动完成明确清理;若协议状态已不确定,我会淘汰该连接,不把“可能还在事务里”的连接交给下一个请求。
冲突后重读重算,不复用旧写入
收到确定的 WATCH 冲突后,我会从新的 WATCH 开始,重新读取当前值,再重新计算和排队。只把第一次算出的 v3 再提交一次,虽然可能不再冲突,却已失去“基于当前输入计算”的意义。
清理状态、重读重算、重试预算应是三个独立约束:设置尝试次数或截止时间,竞争持续时加入有界退避,超过预算就把冲突交给调用方。重试循环内不要提前发送邮件、扣外部账户或调用不可撤销服务,否则 Redis 队列虽未执行,外部副作用却可能重复发生。
网络超时需要另一条处理路径:如果 EXEC 已送达但回复丢失,事务可能已经执行。它不是确定的空回复,盲目重跑非幂等操作可能重复更新。应结合业务标识和结果确认机制处理,而不是统一捕获所有错误后继续循环。

WATCH 不负责执行错误回滚
我不会把 WATCH 介绍成数据库式的“出错全部撤销”。如果监视条件通过,EXEC 开始执行,某条命令因类型不符报错,其他排队命令仍会被处理。此时应检查回复数组的每一项,而非只检查 EXEC 调用有没有返回。
另一个边界是过期:Redis 自身的过期、淘汰也可能使监视失效。Redis 6.0.9 之前,被监视键过期不导致事务中止,这一历史差异不能忽略。测试若带有效期,应记录版本与 TTL,不要把服务器自身修改误判成“莫名其妙的并发写入”。
WATCH 监视的不是一个只允许别的客户端触发的锁。监视客户端在事务之外对键的修改也可能使自己的监视失效;MULTI 中的命令在 EXEC 前只是排队,不能与立即写入混为一谈。
按业务约束选择,不把 WATCH 当互斥锁
| 需求 | 我会优先考虑 | 需要接受的代价 |
|---|---|---|
| 单纯原子加一 | INCR | 使用命令已有的整数语义 |
| 应用基于读取结果计算再更新 | WATCH 配合同连接事务 | 竞争时重读重算,有重试预算 |
| 固定一组命令,无条件读取依赖 | MULTI / EXEC | 仍要处理每条命令错误,不承诺回滚 |
| 禁止其他写入者进入业务临界区 | 另行设计协调机制 | WATCH 不会阻塞对方写入 |
别人修改后又改回原值,还会中止吗?
会发生监视失效;它检测监视期间的修改,而不是只比较最终内容。需要值相等语义时,不应把 WATCH 当成单纯的字符串比较函数。
只 WATCH 没有写入的键有意义吗?
有。只要那个键影响更新决策,就应成为读取依赖的一部分。被监视键不要求一定出现在事务的写命令中。
冲突后直接再 EXEC 可以吗?
不行。本次事务已经结束,监视也已清除。应重新监视、读取、计算和建立事务,而不是重复发 EXEC 或复用旧计算结果。
我的选择条件是:能固定同一连接、读取依赖清楚、竞争可接受且能重算时,WATCH 才是合适的乐观并发控制;无法满足这些条件时,增加重试次数不会修复模型本身。
官方参考:事务语义 https://redis.io/docs/latest/develop/using-commands/transactions/;EXEC 返回类型 https://redis.io/docs/latest/commands/exec/;取消排队 https://redis.io/docs/latest/commands/discard/;取消监视 https://redis.io/docs/latest/commands/unwatch/。
Redis shard Pub/Sub 怎样订阅分片频道
- 上一篇
- Redis shard Pub/Sub 怎样订阅分片频道
- 下一篇
- Redis EVALSHA 找不到脚本时怎样恢复
-
- 数据库 · Redis | 1小时前 | Redis · redis Pub/Sub Redis Cluster SSUBSCRIBE
- Redis shard Pub/Sub 怎样订阅分片频道
- 162浏览 收藏
-
- 数据库 · Redis | 1小时前 | redis idle pending XINFO CONSUMERS inactive Stream消费者组
- Redis XINFO CONSUMERS 的 idle 与 inactive 字段区分指南
- 182浏览 收藏
-
- 数据库 · Redis | 1小时前 |
- Redis 怎样按分数范围读取有序集合
- 199浏览 收藏
-
- 数据库 · Redis | 1小时前 |
- Redis LMOVE 怎样把列表元素转移到另一端
- 420浏览 收藏
-
- 数据库 · Redis | 1小时前 |
- Redis HSCAN 怎样按模式增量读取字段
- 282浏览 收藏
-
- 数据库 · Redis | 1小时前 |
- Redis SET 怎样在写入时返回旧值
- 100浏览 收藏
-
- 数据库 · Redis | 13小时前 | Redis · 数据库 · 分页 · redis groupby limit reduce Redis Search FT.AGGREGATE SORTBY
- Redis Search 怎样按字段聚合结果
- 162浏览 收藏
-
- 数据库 · Redis | 13小时前 |
- Redis 向量检索怎样选择距离度量
- 159浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 521次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 578次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 604次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 554次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 375次使用
-
- Redis Stream XTRIM 如何避免消费组积压无限增长
- 2026-09-12 501浏览
-
- Redis AOF rewrite 期间如何判断磁盘与内存压力
- 2026-09-12 501浏览
-
- Redis RDB 和 AOF 怎么按可接受数据丢失量选择
- 2026-09-08 501浏览
-
- Redis XAUTOCLAIM 之后为什么仍有 pending:JUSTID、PEL 与消息删除边界
- 2026-08-29 501浏览
-
- Redis SET 的 GET 与 KEEPTTL 怎么一起验收:旧值返回、续期与回滚边界
- 2026-08-20 501浏览
