理解 Redis 事务-21(使用事务实现原子操)

news2025/5/31 14:35:46

使用事务实现原子操作

Redis 事务是一种在单个步骤中执行一组命令的机制。"要么全部,要么全部不"的方法确保了数据的一致性和完整性,尤其是在需要对相关数据进行多个操作时。没有事务,并发操作可能会导致竞争条件和不一致的数据状态。本课将探讨如何使用 Redis 事务来实现原子操作,保证事务中的所有命令要么全部执行,要么全部不执行。

理解 Redis 事务

Redis 事务提供了一种将多个命令组合为单个原子操作的方式。这意味着事务中的所有命令都是按顺序执行的,并且与其他客户端隔离。如果事务中的任何命令失败,整个事务将被回滚,以确保数据一致性。

MULTI, EXEC, 和 DISCARD 命令

Redis 事务的核心在于三个命令:MULTI, EXEC, 和 DISCARD

  • MULTI: 该命令标记事务块开始。服务器收到的所有后续命令都将排队在事务内执行,直到发出 EXEC 命令。
  • EXEC:该命令触发事务队列中所有命令的执行。Redis 将按照接收顺序执行命令,并返回一个结果数组。如果在执行阶段任何命令失败(例如,由于语法错误或数据类型不正确),执行会继续,错误将在结果数组中报告。
  • DISCARD:该命令取消事务,清空整个事务队列。不会执行任何命令,连接将恢复到正常状态。

示例:基本交易

让我们说明一个简单的交易,该交易递增两个计数器:counter1counter2

MULTI
INCR counter1
INCR counter2
EXEC

在这个例子中:

  1. MULTI 倡导交易。
  2. INCR counter1INCR counter2 被排队。
  3. EXEC 执行排队中的命令。

结果将是一个包含 counter1counter2 增量值的数组。

示例:与 DISCARD 的交易

现在,让我们看看 DISCARD 是如何工作的。

MULTI
INCR counter1
INCR counter2
DISCARD

在这种情况下,counter1counter2 将不会被递增,因为 DISCARD 命令取消了事务。

原子性在 Redis 事务中

Redis 事务在某种程度上提供了原子性,因为事务中的所有命令都是按顺序且独立执行的。然而,Redis 事务在传统数据库意义上并不提供真正的回滚功能。如果在 EXEC 阶段(例如,由于语法错误或操作了错误的数据类型)有命令失败,Redis 会继续执行队列中的剩余命令。错误会在结果数组中报告,但事务不会被完全回滚。

这种行为与传统 ACID(原子性、一致性、隔离性、持久性)数据库不同,后者在发生故障时会将整个事务回滚到初始状态。Redis 优先考虑性能和简单性,而非完全符合 ACID。

🚫 Redis 不支持回滚的原因

Redis 是单线程、无锁的,其设计目标是高性能和简洁性,而不是像传统数据库那样提供事务隔离和回滚机制(如 ACID 中的 Isolation 和 Durability)。

实现原子操作

Redis 事务对于实现原子操作特别有用,其中多个命令必须作为一个不可分割的整体来执行。这对于在并发环境中保持数据一致性至关重要。

场景:在不同账户之间转账

考虑一个场景,你需要将资金从一个账户转移到另一个账户。这涉及两个操作:减少源账户的余额和增加目标账户的余额。为确保数据一致性,这些操作必须原子性地执行。

这里是如何使用 Redis 事务来实现:

MULTI
DECRBY account1 100  # Subtract 100 from account1
INCRBY account2 100  # Add 100 to account2
EXEC

在这个例子中,如果 EXEC 命令成功,account1 将会减少 100,而 account2 将会增加 100。如果交易被中断或因任何原因失败,这两个操作都不会被执行,以确保资金不会丢失或重复。

场景:实现一个原子计数器

另一种常见的用例是实现一个原子计数器。假设你只想在计数器的当前值低于某个阈值时才进行递增。

MULTI
GET counter
INCR counter
EXEC

然而,这种方法不是原子的。另一个客户端可以在 GETINCR 命令之间增加计数器,从而可能超过阈值。要正确实现这一点,通常需要使用 Lua 脚本(将在下一章节中介绍)或使用 WATCH 命令进行乐观锁(如下文所述)。

使用 WATCH 进行乐观锁

WATCH 命令在 Redis 事务中提供了乐观锁的机制。它允许你监控一个或多个键的变化。如果在调用 EXEC 命令之前,任何被监控的键被修改,事务将被中止。

这是如何使用 WATCH 来实现带阈值的原子计数器:

WATCH counter
GET counter
# Check if the counter is below the threshold
IF counter < threshold THEN
    MULTI
    INCR counter
    EXEC
ELSE
    UNWATCH
ENDIF

在这个例子中:

  1. WATCH counter 监控着 counter 键。
  2. GET counter 获取 counter 的当前值。
  3. 代码检查计数器是否低于阈值。
  4. 如果是,使用 MULTI 开始事务,使用 INCR counter 增加计数器,然后使用 EXEC 执行事务。
  5. 如果计数器不低于阈值,则调用 UNWATCH 命令停止监视该键。
  6. 如果另一个客户端在 WATCHEXEC 命令之间修改了 counter 键,事务将被中止,并且 EXEC 命令将返回 NULL。客户端可以重试该操作。

实现一个简单的速率限制器

速率限制是一种控制用户执行某些操作速率的技术。可以使用 Redis 事务来实现一个简单的速率限制器。

WATCH user:123:requests
GET user:123:requests
IF requests < limit THEN
    MULTI
    INCR user:123:requests
    EXPIRE user:123:requests expiration_time
    EXEC
ELSE
    UNWATCH
    # Reject the request
ENDIF

在这个例子中:

  1. WATCH user:123:requests 监控特定用户的请求次数。
  2. GET user:123:requests 检索当前的请求数量。
  3. 如果请求数量低于限制,则开始一个事务。
  4. 该事务增加请求数量并为该密钥设置过期时间。
  5. 如果请求数量超过限制,请求将被拒绝。

🎯 场景目标:用户从账户 userAuserB 转账 100 元

  • 要求在并发环境中保证数据一致性

  • 防止 userA 在转账过程中余额被其他操作修改

✅ 实现步骤(Redis 乐观锁)

🧱 步骤说明

  1. WATCH userA:监视 userA 的余额

  2. GET userA:读取当前余额

  3. 业务逻辑判断:余额是否充足

  4. MULTI:开启事务

  5. DECRBY userA 100INCRBY userB 100:入队命令

  6. EXEC:提交事务,如果期间 userA 被其他客户端修改,EXEC 会失败


🧪 示例代码(使用 redis-cli 或客户端 SDK 执行)

WATCH userA            # Step 1: 监视 userA 的余额
GET userA              # Step 2: 读取余额(假设是 500)

假设返回结果是 500,则继续进行:

MULTI                  # Step 4: 开启事务
DECRBY userA 100       # Step 5: 扣减 userA
INCRBY userB 100       # 增加 userB
EXEC                   # Step 6: 提交事务

如果 WATCHuserA 没有被修改,EXEC 会成功执行两个命令。

如果在这期间有其他客户端修改了 userA 的值(即使只是 INCR 1 元),EXEC 会失败,整个事务不会执行。


📌 EXEC 返回值说明

  • 成功:[400, 100](表示执行了 DECRBYINCRBY

  • 失败:nil(说明 WATCH 检测到监控键被修改)


💡 实际使用建议(伪代码逻辑)

redis.watch("userA")
balance = redis.get("userA")
if int(balance) >= 100:
    pipe = redis.pipeline()
    pipe.multi()
    pipe.decrby("userA", 100)
    pipe.incrby("userB", 100)
    success = pipe.execute()
    if success:
        print("转账成功")
    else:
        print("余额在事务提交前被其他人改动,重试")
else:
    print("余额不足")

🧱 使用场景总结

使用场景Redis 乐观锁是否合适
用户余额扣减✅ 推荐使用
秒杀库存控制✅ 推荐使用
非强一致性场景(缓存等)❌ 不需要 WATCH
高并发写操作✅ 但注意避免死循环重试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2391696.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

触控精灵 ADB运行模式填写电脑端IP教程

•ADB模式&#xff0c;如果你手机已经root则可以直接运行&#xff0c;无需安装电脑端。 •ADB模式&#xff0c;如果你手机没有root&#xff0c;那你可以windows电脑下载【极限投屏】软件&#xff0c;然后你的手机和电脑的网络要同一个wifi&#xff0c;然后把你电脑的ip地址填写…

uniapp|实现多端图片上传、拍照上传自定义插入水印内容及拖拽自定义水印位置,实现水印相机、图片下载保存等功能

本文以基础视角,详细讲解如何在uni-app中实现图片上传→水印动态编辑→图片下载的全流程功能。 目录 引言应用场景分析(社交媒体、内容保护、企业素材管理等)uniapp跨平台开发优势核心功能实现​图片上传模块多来源支持:相册选择(`uni.chooseImage`)与拍照(`sourceType:…

linux有效裁剪视频的方式(基于ffmpeg,不改变分辨率,帧率,视频质量,不需要三方软件)

就是在Linux上使用OBS Studio录制一个讲座或者其他视频&#xff0c;可能总有些时候会多录制一段时间&#xff0c;但是如果使用剪映或者PR这样的工具在导出的时候总需要烦恼导出的格式和参数&#xff0c;比如剪映就不支持mkv格式的导出&#xff0c;导出成mp4格式的视频就会变得很…

服务器密码安全运维解决新思路:凭据管理SMS+双因素SLA认证结合的方案

引言&#xff1a;云服务器安全成本困局 在云计算渗透率突破60%的今天&#xff0c;中小企业正面临严峻的安全悖论&#xff1a;某权威机构数据显示&#xff0c;72%的云上数据泄露事件源于凭据管理不当&#xff0c;而传统安全解决方案的采购成本往往超过中小企业年利润的8%。这种…

论文阅读笔记——In-Context Edit

ICEdit 论文阅读笔记 指令图像编辑现有方法的局限&#xff1a; 微调类方法&#xff08;InstructPix2Pix、Emu Edit、 Ultra Edit&#xff09;&#xff1a;需要大规模数据和算力、精度高但效率低且泛化性低&#xff1b;免训练方法&#xff08;Prompt-to-Prompt、 StableFlow&am…

【后端高阶面经:MongoDB篇】41、MongoDB 是怎么做到高可用的?

一、MongoDB高可用核心架构&#xff1a;副本集&#xff08;Replica Set&#xff09;设计 &#xff08;一&#xff09;副本集角色与拓扑结构 1. 三大核心角色 角色职责描述资源占用选举权重数据存储Primary唯一接收写请求的节点&#xff0c;将操作日志&#xff08;Oplog&…

DMBOK对比知识点整理(4)

1.常见数据质量维度 常见数据质量维度(DMBOK-P353)质量维度

day12 leetcode-hot100-21(矩阵4)

240. 搜索二维矩阵 II - 力扣&#xff08;LeetCode&#xff09; 1.暴力法O(m*n) 思路&#xff1a;两层for循环即可。 2.二分查找O(m*logn) 思路&#xff1a;每行都用二分查找,因为每行都是排好序的 class Solution {public boolean searchMatrix(int[][] matrix, int targe…

提问:鲜羊奶是解决育儿Bug的补丁吗?

在育儿这个"系统工程"中&#xff0c;过度提醒就像冗余代码&#xff1a;"快写作业"&#xff08;重复调用&#xff09;、"多穿衣服"&#xff08;异常捕获&#xff09;、"别玩手机"&#xff08;进程阻断&#xff09;。羊大师技术育儿实验…

关于数据仓库、数据湖、数据平台、数据中台和湖仓一体的概念和区别

我们谈论数据中台之前&#xff0c; 我们也听到过数据平台、数据仓库、数据湖、湖仓一体的相关概念&#xff0c;它们都与数据有关系&#xff0c;但他们和数据中台有什么样的区别&#xff0c; 下面我们将围绕数据平台、数据仓库、数据湖和数据中台的区别进行介绍。 一、相关概念…

什么是可重组机器人?

可重组机器人是一种具有高度灵活性和适应性的新型机器人系统&#xff0c;能够根据不同任务需求&#xff0c;快速改变自身结构和功能。下面我从概念、结构、特点、应用领域、发展趋势等方面&#xff0c;为你详细介绍&#xff1a; 概念&#xff1a;可重组机器人是由多个标准化、模…

4、docker compose

1、介绍 Docker Compose 是 Docker 官方提供的容器编排工具&#xff0c;用于简化多容器应用的开发、部署和管理。它通过声明式配置文件&#xff08;YAML格式&#xff09;定义容器化应用的服务、网络、存储等组件及其依赖关系&#xff0c;使用户能够通过单一命令快速启动、停止…

SQL里几种JOIN连接

数据信息&#xff1a; 员工表EMP 部门表DEPT 一、INNER JOIN&#xff08;内连接&#xff09; 作用&#xff1a;只返回两个表中完全匹配的行&#xff0c;相当于取交集。 场景&#xff1a;查询「有部门的员工信息」。 示例&#xff1a; SELECT 员工.姓名, 部门.部门名称 FR…

基于通义千问的儿童陪伴学习和成长的智能应用架构。

1.整体架构概览 我们的儿童聊天助手将采用典型的语音交互系统架构,结合大模型能力和外部知识库: 2. 技术方案分解 2.1. 前端应用/设备 选择: 移动App(iOS/Android)、Web应用,或者集成到智能音箱/平板等硬件设备中。技术栈: 移动App: React Native / Flutter (跨平台…

LVS-DR 负载均衡群集

目录 一、LVS-DR集群 1、LVS-DR 工作原理 2、数据包流向分析 3、LVS-DR 模式特点 二、直接路由模式&#xff08;LVS-DR&#xff09; 1、准备案例环境 2、配置负载调度器&#xff08;101&#xff09; &#xff08;1&#xff09;配置虚拟IP 地址&#xff08;VIP&#xff…

[Dify] 如何应对明道云API数据过长带来的Token超限问题

在集成明道云与大型语言模型(LLM)如ChatGPT或本地部署的Dify时,开发者经常会面临一个核心问题:API获取的数据太长,超出LLM支持的Token数限制,导致无法直接处理。本文将深入探讨这个问题的成因,并提供几种可行的解决方案,包括分段处理、外部知识库构建等策略。 明道云AP…

eNSP企业综合网络设计拓扑图

1.拓扑图 2.拓扑配置 此拓扑还有一些瑕疵&#xff0c;仅做参考和技术提升使用。 想要配置的可以关注下载 大型网络综合实验拓扑图&#xff08;eNSP&#xff09;资源-CSDN文库

BugKu Web渗透之备份是个好习惯

启动场景后&#xff0c;网页显示一段字符串。 看起来像md5值&#xff0c;但是又过长了。 步骤一&#xff1a;右键查看源代码&#xff0c;没有发现任何异常。 步骤二&#xff1a;使用dirsearch去查看是否有其他可疑文件。 在终端输入&#xff1a; dirsearch -u http://117.72.…

华为AP6050DN无线接入点瘦模式转胖模式

引言 华为AP6050DN是一款企业级商用的无线接入点。由于产品定位原因,其默认工作在瘦模式下,即须经AC统一控制和管理,是不能直接充当普通的无线路由器来使用的。 而本文的目的,就是让其能脱离AC的统一控制和管理,当作普通无线路由器来使用。 硬件准备 华为AP6050DN无线接…

十、【核心功能篇】项目与模块管理:前端页面开发与后端 API 联调实战

【核心功能篇】项目与模块管理&#xff1a;前端页面开发与后端 API 联调实战 前言准备工作第一部分&#xff1a;完善项目管理功能 (Project)1. 创建/编辑项目的表单对话框组件 第二部分&#xff1a;模块管理功能 (集成到项目详情页)1. 创建模块相关的 API 服务 (src/api/module…