怎么理解 Redis 事务
背景
在面试中经常会被问到,redis支持事务吗?事务是怎么实现的?事务会回滚吗?又是一键三连,我下面分析下,看看能不能吊打面试官
什么是Redis事务
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。 事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
Redis 中的事务如何工作
Redis 事务允许在一个步骤中执行一组命令,它们以 MULTI、EXEC、DISCARD 和 WATCH 命令为中心,事务中的所有命令都被序列化并按顺序执行。另一个客户端发送的请求永远不会在 Redis 事务执行过程中得到服务。这保证了命令作为单个独立操作执行。
一个事务从开始到执行会经历以下三个阶段:
- 开启事务。
- 命令入队。
- 执行事务或者放弃事务。
其中,开启事务使用 multi 命令,事务执行使用 exec 命令,放弃事务使用 discard 命令。
事务执行流程图,可以直观的看下:
Redis事务相关命令和使用
- MULTI :开启事务,redis会将后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令系列。
- EXEC:执行事务中的所有操作命令。
- DISCARD:取消事务,放弃执行事务块中的所有命令。
- WATCH:监视一个或多个key,如果事务在执行前,这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令。
- UNWATCH:取消WATCH对所有key的监视。
MULTI 开启事务
multi 命令用于开启事务,实现代码如下:
# multi 命令可以让客户端从非事务模式状态,变为事务模式状态
127.0.0.1:6371> MULTI
OK
注意啊:multi 命令不能嵌套使用,如果已经开启了事务的情况下,再执行 multi 命令,会提示如下错误:
127.0.0.1:6371> MULTI
OK
127.0.0.1:6371(TX)> MULTI
(error) ERR MULTI calls can not be nested
命令入列
客户端进入事务状态之后,执行的所有常规 Redis 操作命令(非触发事务执行或放弃和导致入列异常的命令)会依次入列,命令入列成功后会返回 QUEUED,如下代码所示
127.0.0.1:6371> MULTI
OK
127.0.0.1:6371(TX)> set k 1
QUEUED
127.0.0.1:6371(TX)> set b 1
QUEUED
用户可以发出多个命令,命令会按照先进先出(FIFO)的顺序出入列,Redis 不会执行这些命令,而是将它们排队。需要调用 EXEC执行事务 ,所有命令才会执行。
执行事务/放弃事务
执行事务的命令是 exec,放弃事务的命令是 discard。
执行事务:
127.0.0.1:6371> MULTI
OK
127.0.0.1:6371(TX)> set k 1
QUEUED
127.0.0.1:6371(TX)> set b 1
QUEUED
127.0.0.1:6371(TX)> EXEC
1) OK
2) OK
EXEC 返回一个回复数组,其中每个元素都是事务中单个命令的回复,与发出命令的顺序相同。
放弃事务:
127.0.0.1:6371> MULTI
OK
127.0.0.1:6371(TX)> set k 2
QUEUED
127.0.0.1:6371(TX)> set b 2
QUEUED
127.0.0.1:6371(TX)> DISCARD
OK
127.0.0.1:6371> get k
"1"
127.0.0.1:6371> get b
"1"
MULTI开启事务后,命令入队,取消事务,队列里面的命令是不会执行的。 设置 k、b两个key的值为2,取消事务后,值还是保留以前是 1,说明取消事务后,队列里面的命令没有执行
事务出现错误的处理
事务执行中的错误分为以下两类:
- 入列的命令语法错误,终止事务
- 入列命令运行时错误,不会终止事务
入列的命令语法错误,终止事务
分别设置 k1、k2的值为1,然后开启事务,设置值k1、k2的值为2,其中 设置k2的时候 命令出错 sets,执行事务,入列的命令未执行,终止了事务
127.0.0.1:6371> set k1 1
OK
127.0.0.1:6371> set k2 1
OK
127.0.0.1:6371> get k1
"1"
127.0.0.1:6371> get k2
"1"
127.0.0.1:6371> MULTI
OK
127.0.0.1:6371(TX)> set k1 2
QUEUED
127.0.0.1:6371(TX)> sets k2 2
(error) ERR unknown command 'sets', with args beginning with: 'k2' '2'
127.0.0.1:6371(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6371> get k1
"1"
127.0.0.1:6371> get k2
"1"
入列的命令运行时错误,不会终止事务
在开启事务后,修改k1值为2,但将k2的类型作为List,进行元素操作,在运行时检测类型错误,最终导致事务提交失败,此时事务并没有回滚,而是跳过错误命令继续执行, 结果k1值改变
127.0.0.1:6371> get k1
"1"
127.0.0.1:6371> get k2
"1"
127.0.0.1:6371> MULTI
OK
127.0.0.1:6371(TX)> set k1 2
QUEUED
127.0.0.1:6371(TX)> LPOP k2
QUEUED
127.0.0.1:6371(TX)> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6371> get k1
"2"
运行时发生错误,事务未终止,事务不会回滚呢?
为什么事务不会回滚?
Redis 官方文档的解释如下:
Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
watch 监控
WATCH 用于为 Redis 事务提供检查和设置 (CAS) 行为。
watch 命令用于客户端并发情况下,为事务提供一个乐观锁(CAS,Check And Set),也就是可以用 watch 命令来监控一个或多个变量,如果在事务的过程中,某个监控项被修改了,那么整个事务就会终止执行,并且 EXEC 返回 Null 回复以通知事务失败
watch 基本语法如下:
watch key [key ...]
举个例子,看下修改监控的key以后,事务是否会终止
客户端1:设置k1的值为1,k2的值1,然后 watch 监控k1,开启事务,设置k1为2,k2为2,客户端2:修改k1的值为23,客户端1执行事务
客户端1执行命令如下:
127.0.0.1:6371> set k1 1
OK
127.0.0.1:6371> set k2 1
OK
127.0.0.1:6371> WATCH k1
OK
127.0.0.1:6371> MULTI
OK
127.0.0.1:6371(TX)> set k1 2
QUEUED
127.0.0.1:6371(TX)> set k2 2
QUEUED
127.0.0.1:6371(TX)> EXEC
(nil)
127.0.0.1:6371> get k1
"3"
127.0.0.1:6371> get k2
"1"
客户端2执行命令如下:
127.0.0.1:6371> set k1 3
OK
从上面的结果可以看出,监控了k1,客户端2修改了k1的值,事务是中止的
如果不再监控key,使用UNWATCH 取消监控
代码实现
使用jedis客户端实现,代码如下:
public static void main(String[] args) {Jedis jedis = new Jedis("10.1.250.157", 6379);jedis.auth("google00");jedis.set("k","1");//监控keyjedis.watch("k");//开启事务Transaction tx =jedis.multi();// 命令入队tx.set("k","2");tx.set("k1","3");//执行事务tx.exec();//取消监控jedis.unwatch();}
总结
redis是支持事务的,开启事务后,命令入队,命令的语法如果有错,执行事务会中止,如果执行命令的时候发现命令有问题,其他命令能正常执行,事务是不会回滚的,因为redis的回滚会对redis的简单性和性能造成严重影响。
特别注意: 事务中的命令要么全部被执行,要么全部都不执行 。是执行,不是成功哦,更我们平时用的关系型数据库是有差别的。
相关文章:

怎么理解 Redis 事务
背景 在面试中经常会被问到,redis支持事务吗?事务是怎么实现的?事务会回滚吗?又是一键三连,我下面分析下,看看能不能吊打面试官 什么是Redis事务 事务是一个单独的隔离操作:事务中的所有命令…...
react中的diff算法
diff算法 对于React团队发现在日常开发中对于更新组件的频率,会比新增和删除的频率更高,所以在diff算法里,判断更新的优先级会更高。对于Vue2的diff算法使用了双指针,React的diff算法没有使用双指针,是因为更新的jsx对…...

【医学大模型 尘肺病】PneumoLLM:少样本大模型诊断尘肺病新方法
PneumoLLM:少样本大模型诊断尘肺病新方法 提出背景PneumoLLM 框架效果 提出背景 论文:https://arxiv.org/pdf/2312.03490.pdf 代码:https://github.com/CodeMonsterPHD/PneumoLLM/tree/main 历史问题及其背景: 数据稀缺性问题&a…...

【SpringBootStarter】自定义全局加解密组件
【SpringBootStarter】 目的 了解SpringBoot Starter相关概念以及开发流程实现自定义SpringBoot Starter(全局加解密)了解测试流程优化 最终引用的效果: <dependency><groupId>com.xbhog</groupId><artifactId>globalValidation-spring…...

【射影几何15】python双曲几何工具geometry_tools
目录 一、说明二、环境问题:如何安装三、实现一个简单的例子四、绘制双曲组五、使用有限状态自动机加快速度六、资源和代码 一、说明 Geometry_tools 是一个 Python 包,旨在帮助您处理和可视化双曲空间和射影空间上的群动作。 该包主要构建在 numpy、…...
机器人抓取 [ 题目/摘要 ] 更新中..
题目:Robotic Grasping of Novel Objects using Visionl 链接:机器人抓取新物体 | IEEE Xplore(IEEE的Xplore) 【端到端】 摘要:我们考虑抓取新物体的问题,特别是第一次通过视觉看到的物体。抓取以前未知的…...

【51单片机】外部中断和定时器中断
目录 中断系统中断介绍中断概念 中断结构及相关寄存器中断结构中断相关寄存器 外部中断实验外部中断配置软件设计实验现象 定时器中断定时器介绍51 单片机定时器原理51 单片机定时/计数器结构51 单片机定时/计数器的工作方式 定时器配置硬件设计软件设计实验现象 中断系统 本章…...

零售行业供应商数据分发,怎样提高安全性和效率?
零售行业是我国经济发展的重要组成,零售行业包罗万象,如包括汽车零售、日化零售、快消品零售等,不同细分行业的运营模式各不相同,但大体来说,零售行业都具备最基础的供应商和零售商,供应商将商品或服务卖给…...

Windows下Node.js下载安装及环境变量配置教程
Windows下Node.js下载安装及环境变量配置教程 安装版本:node-v18.19.0-x64.msi 文章目录 Windows下Node.js下载安装及环境变量配置教程一、Node.js和NPM简介二、下载地址三、安装步骤四、环境配置五、安装淘宝镜像总结 一、Node.js和NPM简介 1、Node.js …...
广义表-C语言
广义表(Generalized List)是一种扩展了线性表的数据结构,它在线性表的基础上增加了元素可以是表的特点。在广义表中,元素不仅可以是单个的数据元素,还可以是一个子表,而子表中的元素也可以是数据元素或其他…...

uniapp+uView 【详解】录音,自制音频播放器
效果预览 代码实现 <template><view class"btnListBox"><view class"audioBox" v-if"audioLength"><u-row><u-col span"2"><u--text aligncenter :text"currentTime"></u--text>…...

机器学习---概率图模型(隐马尔可夫模型、马尔可夫随机场、条件随机场)
1. 隐马尔可夫模型 机器学习最重要的任务是根据已观察到的证据(例如训练样本)对感兴趣的未知变量(例如类别标 记)进行估计和推测。概率模型(probabilistic model)提供了一种描述框架,将描述任…...
cool 框架 node 后端封装三方Api post请求函数
1.需求 现在一些数据源 ,需要从三方地址拿到一些数据 比如说电影列表 信息了 影院列表信息了 等一些展示的数据,但是人家这种东西 害需要使用 appkey appserect 这种验签 这种需求 你前端调用接口是直接调用不了的 因为需要用到验签 需要后端接口转接一…...

awd总结
总结: 由于是第一次参加AWD比赛,各方面经验都不足,在参赛的前几天也是疯狂搜集各种脚本、框架、工具等,同时也参考b站的视频进行学习,我发现就是还是实操才能更快的学习 我觉得就是我前期的准备工作不足,…...
【react】react+es6+antd5.13.2+ts,antd表格的操作如何在父组件写?
reactes6antd5.13.2ts,antd表格的操作如何在父组件写? 我的子组件columns.tsx,只加表头,操作放在父组件。 columns.tsx的代码: export const dataColumns [{title: 项目成员,dataIndex: name,key: name,},{title: 可选账号,alig…...
virtio笔记
最近在看虚拟化相关的东西,以virtio-console为例,记录下。 此文只是学习笔记,文中肯定有不少错误,不要参考 devicemd侧: virtio_console.c中,初始化会对port->cb赋值为 viritio_console_control_tx&am…...

初始web服务器(并基于idea来实现无需下载的tomcat)
前言 前面学习了对应的http协议,我们知道了他是在网络层进行数据传输的协议,负责相应数据以及接收数据的规则,但是在人员开发后端的时候不仅仅需要你写io流进行数据传输,还需要你进行对应的tcp协议来进行数据打包发送http协议-CSD…...

软件文档测试
1 文档测试的范围 软件产品由可运行的程序、数据和文档组成。文档是软件的一个重要组成部分。 在软件的整人生命周期中,会用到许多文档,在各个阶段中以文档作为前阶段工作成果的体现和后阶段工作的依据。 软件文档的分类结构图如下图所示: …...

从零开始手写mmo游戏从框架到爆炸(七)— 消息封装
导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客 上一篇,我们初步把消息handler 注册到了服务中,在进行后续工作之前我们需要再做一些准备工作。 第一:把之前自己管理的bean放到spring中…...
从Unity到Three.js(画线组件line)
JavaScript 0基础,只是照着官方文档临摹了下,之后有时间再进行细节学习和功能封装。 import * as THREE from three; //引入threejsconst renderer new THREE.WebGLRenderer();//创建渲染器 //设置渲染范围,当前撑满全屏,屏幕左上角是&…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

【深度学习新浪潮】什么是credit assignment problem?
Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...

Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
C++ 类基础:封装、继承、多态与多线程模板实现
前言 C 是一门强大的面向对象编程语言,而类(Class)作为其核心特性之一,是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性,包括封装、继承和多态,同时讨论类中的权限控制,并展示如何使用类…...

claude3.7高阶玩法,生成系统架构图,国内直接使用
文章目录 零、前言一、操作指南操作指导 二、提示词模板三、实战图书管理系统通过4o模型生成系统描述通过claude3.7生成系统架构图svg代码转换成图片 在线考试系统通过4o模型生成系统描述通过claude3.7生成系统架构图svg代码转换成图片 四、感受 零、前言 现在很多AI大模型可以…...

React与原生事件:核心差异与性能对比解析
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...