Redis 事务 总结
前言
相关系列
- 《Redis & 目录》(持续更新)
- 《Redis & 事务 & 源码》(学习过程/多有漏误/仅作参考/不再更新)
- 《Redis & 事务 & 总结》(学习总结/最新最准/持续更新)
- 《Redis & 事务 & 问题》(学习解答/持续更新)
参考文献
- 《Redis事务详解》
概述
Redis事务并不严格具备ACID属性。在我们对数据库的常规理解中,事务必然会严格遵守原子性/一致性/隔离性/持久性。但由于Redis基于内存设计/实现的原因,其对事务ACID属性的实现并不存在/彻底。因此与其称之为事务,将之称为批处理其实更加贴切…该知识点会在下文讲解ACID时详述。
Redis事务的意义是为了防止插队。Redis事务中的指令会被打包一次执行,因此可以避免多客户端指令交互执行的情况发生。
使用
开启/执行/取消
事务的开启/执行/取消指令具体如下:
- MULTI:开启事务;
- EXEC:执行事务;
// ---- 设置一个键/值为num/1的键值对。
> SET num 1
OK
// ---- 开启事务,并依次递增/递减/递增。每个指令输入后都会返回QUEUED,意味着指令已被成功加入
// 事务队列中,等待最终被执行。
> MULTI
OK
> INCR num
QUEUED
> DECR num
QUEUED
> INCR num
QUEUED
// ---- 执行事务,并返回每条指令的执行结果。
> EXEC
2
1
2
- DISCARD:在执行前取消事务。
> SET num 1
OK
// ---- 开启事务,并添加递增/递减/递增指令。
> MULTI
OK
> INCR num
QUEUED
> DECR num
QUEUED
> INCR num
QUEUED
// ---- 取消事务。
> DISCARD
OK
// ---- 取消事务后再执行会抛出错误。
> EXEC
(error) ERR EXEC without MULTI
阶段
Redis事务分为组队/执行两个阶段。组队阶段是Redis事务的第一阶段,由指令“MULTI”在开启事务时同步开启。在组队阶段中Redis会创建相应的事务队列用于顺序接收客户端发送的各项指令,因此事务中的指令其实是多次发送的。而由于Redis不可能在客户端发送全部指令/发送“EXEC”指令/发送“DISCARD”前无限等待,因为整个事务指令发送过程的时长并不可控,因此Redis实际上可以并发接受多个客户端指令。这种并发接收可能是单线程在多客户端之间相互切换实现的,也可能真的有多个线程在各自为政,这具体与Redis的版本有关。随着Redis的不断发展,早期(6.0之前)版本的“单线程实现”描述其实已经不太准确了,就比如现今绝大多数版本的Redis都会启用额外线程来实现数据持久化。但一直以来始终不变的是指令必然只会由唯一的主线程执行,因此在该设计被破坏前,无论Redis如何引入多线程,其都可以被称为单线程实现。
Redis事务可能无法进入执行阶段。执行阶段顾名思义,即是将已完成组队的事务指令整体交由主线程依次执行。而由于执行阶段会由“EXEC”负责开启,因此如果客户端事先发送了“DISCARD”指令,那么事务/事务队列就会直接结束/丢弃而无法进入执行阶段。而除此之外如果客户端发送的指令出现语法错误,那事务也会因为原子性而直接结束…该知识点会在下文讲解原子性时详述。
实现
原子性
Redis事务具备“伪”原子性。原子性通俗的说法就是“要么一起成功,要么一起失败”,关于Redis事务的原子性一直是相当具有争议的点,原因就是其未曾“完全”遵循原子性的定义,即其允许在某些情况下部分成功。因此Redis具备原子性虽然属于官方说法,但仍有部分开发者坚持认为其并不具备原子性。而对于作者本人而言我认为Redis是具有原子性的,只是基于其功能定位而难以/无法实现的足够彻底而已,因此我将这种原子性称为“伪”原子性。
Redis事务在组队阶段具备“真”原子性。对于在组队阶段的事务而言,如果客户端输入的指令在语法上存在错误,那么其整个事务/事务队列都会直接结束/丢弃。该行为符合原子性的标准定义,因此Redis事务在组队阶段具备“真”原子性…具体图示/案例如下:
> SET num 1
OK
> MULTI
OK
> INCR num
QUEUED
// ---- 错误指令,不同版本的Redis/图形客户端在此会有不同的表现,我这里显示排队成功,但有些版本会直接报错。
> INCR1 num
QUEUED
> INCR num
QUEUED
// ---- 执行时由于存在错误指令会抛出异常,整体不执行。
> EXEC
ReplyError: EXECABORT Transaction discarded because of previous errors.
Redis事务在执行阶段具备“伪”原子性。对于在执行阶段的事务而言,即使其在过程中出现某指令执行异常的情况,Redis也只会记录该异常并继续执行后续指令,因此Redis事务并不具备数据回滚能力。该行为明显违背了原子性的标准定义,因此Redis事务在执行阶段只具备“伪”原子性…具体图示/案例如下:
> SET name aaa
OK
> MULTI
OK
> SET name bbb
QUEUED
// ---- 该指令没有语法错误,但字符串类型是无法递增的。
> INCR name
QUEUED
> SET name ccc
QUEUED
// ---- 执行后只有两个结果,因为第二条指令会执行失败。可能是因为Redis版本/图形客户端
// 的原因,我这里没有抛出异常。
> EXEC
OK
OK
一致性/持久性
Redis事务具备结构一致性,但不具备内容一致性。Redis并不像Mysql一样对数据存在为空/大小/长度/范围/唯一/外键等结构性约束,故而事务数据只要计算成功其结构就必然合理,因此Redis事务具备结构一致性。但与此同时Redis事务却不具备内容一致性,因为其“伪”原子性无法保证所有指令全都执行成功。例如Redis事务就无法保证账号A/B在收/支上的一致性,因为两者的指令都存在失败可能,而任意一方的失败都将导致收支上的不平衡,因为Redis事务不具备内容一致性。
Redis事务不(直接)具备持久性。所谓持久性是指数据被永久保存的特性,Redis存在RDB/AOF两种持久化机制,在两者被打开至少一个的情况下,Redis可以将内存中的数据持久化至磁盘中永久保存,从而“尽可能”避免在宕机/关机/重启/断电时丢失数据。该知识点会在Redis持久化的专项文章中详述,此处我们只需知道的是:虽然Redis具备全局性的数据持久化能力,但遗憾的是该机制的运行与事务是完全独立的,即事务的执行并不会强制触发该机制以实现对事务数据的持久化。因此Redis事务是不具备持久性的,虽然全局性的持久化机制可以在一定程度上间接达成这个效果。
隔离性/WATCH
Redis事务具备“读已递交”级别的隔离性。所谓隔离性是指事务并发期间的相互影响程度,这种相互影响在数据库的标准定义中被从低到高的被分为四个级别,其具体名称/问题/效果如下所示。而Redis没有设计任何专项机制去提升事务的隔离性,因此Redis事务仅天然具有“读已递交”级别的隔离性。为什么不是“读未递交”级别的隔离性呢?这是因为Redis事务压根没有统一递交的说法…其指令的每次执行都可以直接视作数据的一次递交。
名称 | 问题 | 效果 |
---|---|---|
读未递交 | 脏读/不可重复度/幻读 | 会读取到其它事务未递交的数据,并且即无法保证数据的内容不变化,也无法保证数据的数量不变化 |
读已递交 | 不可重复度/幻读 | 不会读取到其它事务未递交的数据,但即无法保证数据的内容不变化,也无法保证数据的数量不变化 |
可重复读 | 幻读 | 不会读取到其它事务未递交的数据,并且可以保证数据的内容不变化,但无法保证数据的数量不变化 |
序列化 | / | 事务串行执行,相互之间完全不影响 |
Redis事务在执行阶段具备“序列化”等级的隔离级别。由于单线程实现的原因,Redis事务在执行阶段其实具备“序列化”等级的隔离级别。因为在此期间其它事务最多只能处于组队阶段而无法处于执行阶段,甚至在早期版本中都可能不存在其它事务,因此对于当前正在执行的事务来说是绝对不可能出现目标数据被并发修改的情况的。那为什么说Redis事务只具备“读已递交”级别的隔离性呢?这是因为如果反过来看正处于组队阶段的当前事务,那么其目标数据就完全可能被处于执行阶段的其它事务所并发修改,从而导致出现执行前提被破坏的“竞态条件”情况…具体示例时间表如下:
时间 | 事务1 | 事务2 |
---|---|---|
T1 | 商品总量为5000,开启事务 | 商品总量为5000,开启事务 |
T2 | 组队指令 – 设置商品总量为4000(-1000) | 组队指令 – 设置商品总量为3000(-2000) |
T3 | 单线程执行指令 – 商品总量被设置为3000T4 | 单线程执行指令 – 商品总量被设置为4000但正确总量应该为2000 |
Redis提供了乐观锁机制的{WTACH}指令用于监控事务的目标数据。上述问题的本质还是线程安全问题,其核心在于指令的执行依据被并发修改,因此只要能够控制这一点就能为事务的正确执行提供基础可能。对此Redis基于乐观锁机制提供了{WTACH}指令用于监控目标数据,其本质是记录目标数据的快照。而当事务进入执行阶段时其会将最新的目标数据与快照进行对比,并在发现目标数据已变化的情况下拒绝执行事务并返回null…具体示例如下:
- WATCH key [key …]:监控目标数据集,并在目标数据发生变化时拒绝执行事务。
------------------------- 入参 -------------------------
key [key …] @ 键集:需监控目标数据集的键集,多个键之间使用" "分割。
// ---- 设置/监控目标数据,并开启事务。
> SET goal aaaa
OK
> WATCH goal
OK
> MULTI
OK
// ---- 更新目标数据。
> SET goal bbbb
QUEUED
// ---- 如果目标数据在事务组队期间被其它线程/事务修改,当前事务将不会执行并返回null。
> EXEC
null
管道
管道可大幅提升多指令的执行效率。管道相当于将事务的组队阶段从服务端转移到了客户端执行,因此其不会像事务一样多次发送指令,而是会直接将已完成打包/组队的命令集一次性发送至服务端执行。而又因为Redis执行的高效性会使得指令/数据的RRT @ Round Trip Time @ 往返时间/网络传输时间远远多于指令的执行时间,因此多指令/数据的一次性发送/返回能够有效提升批量操作的执行效率,也因此管道被广泛用于数据大批量导入/导出的场景中。
Redis管道无法通过原生指令开启…下文展示的是通过Jedis使用管道的模板:
public static void testPipeLineAndNormal(Jedis jedis) throws InterruptedException {// ---- 开启管道。Pipeline pipeline = jedis.pipelined();// ---- 在管道中添加多个SET指令。for (int i = 0; i < 10000; i++) {pipe.set(String.valueOf(i), String.valueOf(i));}// ---- 一次性发送/执行管道指令。pipe.sync();
}
相关文章:

Redis 事务 总结
前言 相关系列 《Redis & 目录》(持续更新)《Redis & 事务 & 源码》(学习过程/多有漏误/仅作参考/不再更新)《Redis & 事务 & 总结》(学习总结/最新最准/持续更新)《Redis & 事务…...
go sdk的安装或者升级
背景 由于 go 语言的官方sdk还在不断的更新迭代中,有的时候相对应的生态相关的依赖包也在不断的升级,如果很长一段时间不升级自己的本地的go sdk 那么就有可能在拉取代码的时候出现错误,因此有的时候可能需要我们适当的升级下自己的sdk&…...
mongo实操笔记
这个链接我用了其在Windows下的下载安装 是可以的 ,不过我太懒了,没有弄成自启动 Windows安装MongoDB_mongodb windows安装-CSDN博客 下面这个链接就更好了,我用了其与springboot整合的测试。可以直接操作mongodb了。 SpringBoot整合Mongo…...

前端算法:树(力扣144、94、145、100、104题)
目录 一、树(Tree) 1.介绍 2.特点 3.基本术语 4.种类 二、树之操作 1.遍历 前序遍历(Pre-order Traversal):访问根节点 -> 遍历左子树 -> 遍历右子树。 中序遍历(In-order Traversal…...
深度学习速通系列:如何使用bert进行超长中文文本命名实体识别
要将超长中文文本按最大 BERT 输入长度进行分割,并使用 bert-chinese-ner 模型进行命名实体识别,可以遵循以下步骤。以下是一个 Python 代码示例,利用 Hugging Face 的 transformers 库来实现: 安装必要的库 如果你还没有安装 Hu…...
【感知模块】深度神经网络实现运动预测
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言运动预测(Motion Prediction)感知中的运动预测(深度神经网络)前言 认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长! …...

智能优化算法-蝗虫优化算法(GOA)(附源码)
目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍 蝗虫优化算法 (Grasshopper Optimization Algorithm, GOA) 是一种基于群体智能的元启发式优化算法,由Saremi等人于2017年提出。GOA模拟了蝗虫群的觅食、迁徙和社会互动行为,用于解决复杂…...

TVM前端研究--Relay
文章目录 深度学习IR梳理1. IR属性2. DL前端发展3. DL编译器4. DL编程语言Relay的主要内容一、Expression in Relay1. Dataflow and Control Fragments2. 变量3. 函数3.1 闭包3.2 多态和类型关系3.3. Call4. 算子5. ADT Constructors6. Moudle和Global Function7. 常量和元组8.…...
STM32外设应用
STM32是基于ARM Cortex-M系列内核的微控制器,具有高性能、低功耗和丰富的外设资源。其广泛应用于物联网、工业控制、智能家居和嵌入式系统等领域。本文将简要介绍STM32常用外设的功能及应用实例,帮助大家更好地理解和使用STM32外设。 1. GPIO࿰…...
Docker 部署 Jaeger
Jaeger 的主要作用如下: 分布式追踪 Jaeger 是一个开源的分布式追踪系统,用于监控和排查微服务架构中的复杂问题。它可以跟踪请求在不同服务之间的传播路径,帮助开发者理解系统中各个组件之间的调用关系。 性能分析 通过收集和分析请求的执行…...

使用Python和OpenCV实现火焰检测
使用Python和OpenCV实现火焰检测 项目解释: 此 Python 代码是使用 OpenCV、线程、声音和电子邮件功能的火灾探测系统的简单示例。 以下是它的功能的简单描述: 导入库:代码首先导入必要的库: cv2:用于图像和视频处理…...
uniapp基础笔记
与html区别 uni-app简单来说是 vue的语法 小程序的api。 文件结构 html <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><script type"text/javascript"></script><style t…...

函数基础,定义与调用。作用域,闭包函数
一、函数的定义与调用 函数是一段可重复使用的代码块,用于执行特定任务或计算等功能。它可以接受输入参数(形参),并根据参数执行操作后返回结果。 函数的定义 例如在 JavaScript 中可以这样定义函数: function fun…...

【Linux网络编程】 --- Linux权限理解
Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: Linux网络编程 🏠 shell命令以及运行原理 📌 引入例子理解shell 假设八里村有一个人叫张三,他的父亲是这个村的村长…...

Qt/C++ 调用迅雷开放下载引擎(ThunderOpenSDK)下载数据资源
目录导读 前言ThunderOpenSDK 简介参考 xiaomi_Thunder_Cloud 示例ThunderOpenSDK 下载问题 前言 在对以前老版本的exe执行程序进行研究学习的时候,发现以前的软件是使用的ThunderOpenSDK这个迅雷开放下载引擎进行的项目数据下载,于是在网上搜索一番找到…...
深入详解 Java - Spring MVC
在 Java 企业级开发领域,Spring MVC 是一个极为重要的框架,它为构建强大、灵活且高效的 Web 应用程序提供了坚实的基础。本文将深入详解 Java 之 Spring MVC,带你领略其强大之处。 一、Spring MVC 概述 Spring MVC 是 Spring 框架的一个重要模块,全称为 Spring Web Model-V…...

Spring Boot技术中小企业设备管理系统设计与实践
6系统测试 6.1概念和意义 测试的定义:程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为: 目的:发现程序的错误; 任务:通过在计算机上执行程序,暴露程序中潜在的错误。 另一个…...
动态渲染组件
引言 在现代前端开发中,动态渲染组件是一种常见的需求,特别是在构建复杂的应用程序时。动态渲染组件允许我们在运行时根据不同的条件或数据来决定渲染哪个组件,从而提高代码的灵活性和可维护性。本文将详细介绍如何在 Vue.js 中实现动态渲染…...

一个神秘的新图像生成模型red_panda出现 轻松击败Midjourney与OpenAI
一个神秘的新图像生成模型在众包人工分析基准测试中击败了 Midjourney、黑森林实验室和 OpenAI 的模型。这个名为"red_panda"的模型在人工分析的文本到图像排行榜上领先排名第二的黑森林实验室的 Flux1.1 Pro 约 40 个 Elo 分数。 Artificial Analysis 使用 Elo&…...

云计算平台上的DevOps实践
文章目录 什么是DevOps云计算平台上的DevOps优势自动化部署弹性伸缩地理分布 实施DevOps的关键组件版本控制系统持续集成/持续交付工具配置管理工具监控和日志管理 实践案例使用AWS CodePipeline进行持续集成/持续交付利用AWS Auto Scaling实现弹性使用AWS CloudFormation进行基…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

uni-app学习笔记三十五--扩展组件的安装和使用
由于内置组件不能满足日常开发需要,uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件,需要安装才能使用。 一、安装扩展插件 安装方法: 1.访问uniapp官方文档组件部分:组件使用的入门教程 | uni-app官网 点击左侧…...
当下AI智能硬件方案浅谈
背景: 现在大模型出来以后,打破了常规的机械式的对话,人机对话变得更聪明一点。 对话用到的技术主要是实时音视频,简称为RTC。下游硬件厂商一般都不会去自己开发音视频技术,开发自己的大模型。商用方案多见为字节、百…...