理解 Redis 事务-21(使用事务实现原子操)
使用事务实现原子操作
Redis 事务是一种在单个步骤中执行一组命令的机制。"要么全部,要么全部不"的方法确保了数据的一致性和完整性,尤其是在需要对相关数据进行多个操作时。没有事务,并发操作可能会导致竞争条件和不一致的数据状态。本课将探讨如何使用 Redis 事务来实现原子操作,保证事务中的所有命令要么全部执行,要么全部不执行。
理解 Redis 事务
Redis 事务提供了一种将多个命令组合为单个原子操作的方式。这意味着事务中的所有命令都是按顺序执行的,并且与其他客户端隔离。如果事务中的任何命令失败,整个事务将被回滚,以确保数据一致性。
MULTI
, EXEC
, 和 DISCARD
命令
Redis 事务的核心在于三个命令:MULTI
, EXEC
, 和 DISCARD
。
MULTI
: 该命令标记事务块开始。服务器收到的所有后续命令都将排队在事务内执行,直到发出EXEC
命令。EXEC
:该命令触发事务队列中所有命令的执行。Redis 将按照接收顺序执行命令,并返回一个结果数组。如果在执行阶段任何命令失败(例如,由于语法错误或数据类型不正确),执行会继续,错误将在结果数组中报告。DISCARD
:该命令取消事务,清空整个事务队列。不会执行任何命令,连接将恢复到正常状态。
示例:基本交易
让我们说明一个简单的交易,该交易递增两个计数器:counter1
和 counter2
。
MULTI
INCR counter1
INCR counter2
EXEC
在这个例子中:
MULTI
倡导交易。INCR counter1
和INCR counter2
被排队。EXEC
执行排队中的命令。
结果将是一个包含 counter1
和 counter2
增量值的数组。
示例:与 DISCARD
的交易
现在,让我们看看 DISCARD
是如何工作的。
MULTI
INCR counter1
INCR counter2
DISCARD
在这种情况下,counter1
和 counter2
将不会被递增,因为 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
然而,这种方法不是原子的。另一个客户端可以在 GET
和 INCR
命令之间增加计数器,从而可能超过阈值。要正确实现这一点,通常需要使用 Lua 脚本(将在下一章节中介绍)或使用 WATCH
命令进行乐观锁(如下文所述)。
使用 WATCH
进行乐观锁
WATCH
命令在 Redis 事务中提供了乐观锁的机制。它允许你监控一个或多个键的变化。如果在调用 EXEC
命令之前,任何被监控的键被修改,事务将被中止。
这是如何使用 WATCH
来实现带阈值的原子计数器:
WATCH counter
GET counter
# Check if the counter is below the threshold
IF counter < threshold THENMULTIINCR counterEXEC
ELSEUNWATCH
ENDIF
在这个例子中:
WATCH counter
监控着counter
键。GET counter
获取 counter 的当前值。- 代码检查计数器是否低于阈值。
- 如果是,使用
MULTI
开始事务,使用INCR counter
增加计数器,然后使用EXEC
执行事务。 - 如果计数器不低于阈值,则调用
UNWATCH
命令停止监视该键。 - 如果另一个客户端在
WATCH
和EXEC
命令之间修改了counter
键,事务将被中止,并且EXEC
命令将返回NULL
。客户端可以重试该操作。
实现一个简单的速率限制器
速率限制是一种控制用户执行某些操作速率的技术。可以使用 Redis 事务来实现一个简单的速率限制器。
WATCH user:123:requests
GET user:123:requests
IF requests < limit THENMULTIINCR user:123:requestsEXPIRE user:123:requests expiration_timeEXEC
ELSEUNWATCH# Reject the request
ENDIF
在这个例子中:
WATCH user:123:requests
监控特定用户的请求次数。GET user:123:requests
检索当前的请求数量。- 如果请求数量低于限制,则开始一个事务。
- 该事务增加请求数量并为该密钥设置过期时间。
- 如果请求数量超过限制,请求将被拒绝。
🎯 场景目标:用户从账户 userA
向 userB
转账 100 元
-
要求在并发环境中保证数据一致性
-
防止
userA
在转账过程中余额被其他操作修改
✅ 实现步骤(Redis 乐观锁)
🧱 步骤说明
-
WATCH userA
:监视userA
的余额 -
GET userA
:读取当前余额 -
业务逻辑判断:余额是否充足
-
MULTI
:开启事务 -
DECRBY userA 100
、INCRBY userB 100
:入队命令 -
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: 提交事务
如果 WATCH
后 userA
没有被修改,EXEC
会成功执行两个命令。
如果在这期间有其他客户端修改了 userA
的值(即使只是 INCR
1 元),EXEC
会失败,整个事务不会执行。
📌 EXEC 返回值说明
-
成功:
[400, 100]
(表示执行了DECRBY
和INCRBY
) -
失败:
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 |
高并发写操作 | ✅ 但注意避免死循环重试 |
相关文章:
理解 Redis 事务-21(使用事务实现原子操)
使用事务实现原子操作 Redis 事务是一种在单个步骤中执行一组命令的机制。"要么全部,要么全部不"的方法确保了数据的一致性和完整性,尤其是在需要对相关数据进行多个操作时。没有事务,并发操作可能会导致竞争条件和不一致的数据状…...
神经网络加上注意力机制,精度反而下降,为什么会这样呢?注意力机制的本质是什么?如何正确使用注意力机制?注意力机制 | 深度学习
在深度学习的发展中,注意力机制的引入曾被誉为一次划时代的技术飞跃。无论是在自然语言处理领域产生Transformer架构,还是在图像识别、语音识别和推荐系统等多个方向取得显著成效,注意力机制的价值似乎毋庸置疑。然而,在一些实际应用场景中,研究人员和工程师却发现:在传统…...

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

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

linux有效裁剪视频的方式(基于ffmpeg,不改变分辨率,帧率,视频质量,不需要三方软件)
就是在Linux上使用OBS Studio录制一个讲座或者其他视频,可能总有些时候会多录制一段时间,但是如果使用剪映或者PR这样的工具在导出的时候总需要烦恼导出的格式和参数,比如剪映就不支持mkv格式的导出,导出成mp4格式的视频就会变得很…...

服务器密码安全运维解决新思路:凭据管理SMS+双因素SLA认证结合的方案
引言:云服务器安全成本困局 在云计算渗透率突破60%的今天,中小企业正面临严峻的安全悖论:某权威机构数据显示,72%的云上数据泄露事件源于凭据管理不当,而传统安全解决方案的采购成本往往超过中小企业年利润的8%。这种…...

论文阅读笔记——In-Context Edit
ICEdit 论文阅读笔记 指令图像编辑现有方法的局限: 微调类方法(InstructPix2Pix、Emu Edit、 Ultra Edit):需要大规模数据和算力、精度高但效率低且泛化性低;免训练方法(Prompt-to-Prompt、 StableFlow&am…...
Debian 系统 Python 开发全解析:从环境搭建到项目实战
Debian 系统 Python 开发全解析:从环境搭建到项目实战 在当今数字化时代,Python 凭借其简洁易读的语法和强大的功能,成为了最受欢迎的编程语言之一。Debian 作为一款稳定、安全且开源的 Linux 操作系统,为 Python 开发提供了理想的环境。本文将详细介绍在 Debian 系统上进…...
Next.js 15 与 Apollo Client 的现代集成及性能优化
Next.js 15 与 Apollo Client 的现代集成及性能优化 目录 技术演进集成实践性能优化应用案例未来趋势 技术演进 Next.js 15 核心特性对开发模式的革新 Next.js 15 通过引入 App Router、服务器组件(Server Components)和客户端组件(Clie…...

【后端高阶面经:MongoDB篇】41、MongoDB 是怎么做到高可用的?
一、MongoDB高可用核心架构:副本集(Replica Set)设计 (一)副本集角色与拓扑结构 1. 三大核心角色 角色职责描述资源占用选举权重数据存储Primary唯一接收写请求的节点,将操作日志(Oplog&…...
IO Vs NIO
一、IO(传统阻塞式) 全称:Input/Output(输入/输出) 定义:Java 1.0 引入的基础 I/O 模型,基于流(Stream)的同步阻塞操作,线程在读写数据时会阻塞直到操作完成。 二、NIO(新式非阻塞式) 全…...
offset 家族和 client 家族
在前端开发中,offset 家族和 client 家族是用于获取元素尺寸和位置的重要属性集合。以下是对这两个家族相关知识点的系统总结: 一、offset 家族 核心属性 offsetWidth / offsetHeight 含义:元素的总尺寸,包含内容区、内边距、边…...

DMBOK对比知识点整理(4)
1.常见数据质量维度 常见数据质量维度(DMBOK-P353)质量维度...

day12 leetcode-hot100-21(矩阵4)
240. 搜索二维矩阵 II - 力扣(LeetCode) 1.暴力法O(m*n) 思路:两层for循环即可。 2.二分查找O(m*logn) 思路:每行都用二分查找,因为每行都是排好序的 class Solution {public boolean searchMatrix(int[][] matrix, int targe…...
Java基础 Day24
一、进程和线程 1、进程 (1)概念 进程 (Process) 是计算机中的程序关于某数据集合上的一次运行活动 是系统进行资源分配的基本单位 简单理解:程序的执行过程(正在运行的应用程序) (2)特性…...

提问:鲜羊奶是解决育儿Bug的补丁吗?
在育儿这个"系统工程"中,过度提醒就像冗余代码:"快写作业"(重复调用)、"多穿衣服"(异常捕获)、"别玩手机"(进程阻断)。羊大师技术育儿实验…...

关于数据仓库、数据湖、数据平台、数据中台和湖仓一体的概念和区别
我们谈论数据中台之前, 我们也听到过数据平台、数据仓库、数据湖、湖仓一体的相关概念,它们都与数据有关系,但他们和数据中台有什么样的区别, 下面我们将围绕数据平台、数据仓库、数据湖和数据中台的区别进行介绍。 一、相关概念…...
Hive 分桶(Bucketing)深度解析:原理、实战与核心概念对比
一、分桶的意义:比分区更细的粒度管理 1.1 解决分区数据不均匀问题 分区的局限性:分区基于表外字段(如时间字段)划分数据,但可能导致部分分区数据量过大,部分过小,无法进一步细化。 分桶的定…...
网络协议DHCP
DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一种网络协议,用于自动给网络中的设备分配 IP 地址、子网掩码、默认网关、DNS 服务器等网络配置参数。 ✅ 一、DHCP 的作用 自动为客户端分配网络信息,…...

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

4、docker compose
1、介绍 Docker Compose 是 Docker 官方提供的容器编排工具,用于简化多容器应用的开发、部署和管理。它通过声明式配置文件(YAML格式)定义容器化应用的服务、网络、存储等组件及其依赖关系,使用户能够通过单一命令快速启动、停止…...
Node.js全局对象详解:console、process与核心功能
在Node.js开发中,全局对象是一类无需引入即可直接访问的对象,它们为开发者提供了与运行时环境、系统交互和调试相关的核心功能。本文将深入解析Node.js中两个最常用的全局对象 console 和 process,并简要介绍其他全局对象的作用。通过代码示例…...
测试策略:AI模型接口的单元测试与稳定性测试
测试策略:AI模型接口的单元测试与稳定性测试 在构建支持AI能力的系统中,开发者不仅要关注业务逻辑的正确性,也必须保障AI模型接口在各种环境下都能稳定运行。这就要求我们在开发阶段制定清晰的测试策略,从功能验证到性能保障,逐步推进系统可用性、可维护性与可扩展性的提…...

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

基于通义千问的儿童陪伴学习和成长的智能应用架构。
1.整体架构概览 我们的儿童聊天助手将采用典型的语音交互系统架构,结合大模型能力和外部知识库: 2. 技术方案分解 2.1. 前端应用/设备 选择: 移动App(iOS/Android)、Web应用,或者集成到智能音箱/平板等硬件设备中。技术栈: 移动App: React Native / Flutter (跨平台…...
生产环境Mysql推荐配置参数
以下是针对生产环境的 MySQL 配置(my.cnf 或 mysqld.cnf)推荐配置及说明。请根据实际硬件资源(如内存、CPU、磁盘类型)和应用场景调整参数。 核心配置模板(InnoDB 优化) [mysqld] #---------------------- 基础设置 ---------------------- datadir = /var/lib…...

LVS-DR 负载均衡群集
目录 一、LVS-DR集群 1、LVS-DR 工作原理 2、数据包流向分析 3、LVS-DR 模式特点 二、直接路由模式(LVS-DR) 1、准备案例环境 2、配置负载调度器(101) (1)配置虚拟IP 地址(VIPÿ…...
理解并解决高丢包率问题,构建清晰流畅的实时音视频通话
丢包作为数字通信中的重要干扰因素,常常潜伏在表面之下,却严重影响性能,将清晰的对话变的模糊不清,将连贯的演示变的断断续续。因此,对音视频通话相关应用的开发者来说,理解丢包率非常重要。 什么是丢包&am…...
Ubuntu系统Todesk进度卡在100%
1 : 修改配置文件,关闭wayland sudo nano /etc/gdm3/custom.conf2 : 把#WaylandEnablefalse前的#号删掉 按图片删除注释 3 : 按Ctrl X ,离开(会问你要不要保存,输入 Y 回车保存) 4 : 重启系统 或在命令行输入 r…...

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