图解 Redis 事务 ACID特性 |源码解析|EXEC、WATCH、QUEUE

写在前面

Redis 通过 MULTIEXECWATCH 等命令来实现事务功能。Redis的事务是将多个命令请求打包,然后一次性、按照顺序的执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而该去执行其他客户端的命令请求。 就像下面这样:

redis> MULTI
OK
redis(TX)> SET fanone 1
QUEUED
redis(TX)> SET fantwo 2
QUEUED
redis(TX)> GET fanone
QUEUED
redis(TX)> EXEC
1) OK
2) OK
3) "1"

本文我们就从redis的事务执行过程以及ACID四个方面来介绍redis的事务

事务实现

在这里插入图片描述
从上面的例子我们可以知道,redis的事务是从MULTI命令开始的,所有的命令都会按照FIFO的顺序进入一个QUEUE队列中,当执行EXEC操作后才将这些命令逐步执行。

MULTI

事务队列是一个multiCmd类型的数组,数组中的每个multiCmd结构都保存了一个已入队命令的相关信息,包括指向命令实现函数的指针、命令的参数,以及参数的数量:

typedef struct multiCmd{robj **argv; 	// 参数int argc; 		// 参数数量struct redisCommand *cmd // 命令指针
} multiCmd;

事务队列以FIFO先进先出的方式保存入队的命令,还是用上面的例子来画一个原型图:

在这里插入图片描述
当一个处于事务状态的客户端向服务器发送EXEC命令的时候,这个EXEC命令将立即被服务器执行。服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行命令所得的结果全部返回给客户端。EXEC的伪代码如下:

void EXEC() {std::vector<Reply> reply_queue;  // 创建空白的回复队列for (const auto& cmd : client.mstate.commands) { // 执行事务中的所有命令Reply reply = execute_command(cmd.command, cmd.argv, cmd.argc);reply_queue.push_back(reply);}client.flags &= ~REDIS_MULTI; // 清理事务状态client.mstate.count = 0;release_transaction_queue(client.mstate.commands);send_reply_to_client(client, reply_queue);// 发送回复给客户端
}

WATCH

接着我们来讲讲WATCH命令,其实这是一个乐观锁(optimistic locking),可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否有已经被修改过了的,如果是的话,服务器将拒绝执行事务,并向客户端返回代表事务执行失败的空回复

在这里插入图片描述
比如下面这个例子:
在这里插入图片描述

时间客户端A客户端B
T1WATCH “fanone”
T2MULTI
T3SET “fanone” 1
T4SET “fanone” 2
T5EXEC

而在时间T4,客户端B修改了fanone键的值,当客户端A在T5执行EXEC命令的时候,服务器会发现WATCH监视的键fanone已经被修改了,因此服务器拒绝执行客户端A的事务,并且向客户端A返回空回复

每个Redis数据库都保存着一个watched_keys字典,这个字典的键是某个被WATCH命令监视的数据库键,而字典的值则是一个链表,链表中记录了所有监视数据库键的客户端。

typedef struct redisDb {dict *watched_keys; // 正在被WATCH命令监视的键 
}

在这里插入图片描述

ACID

Redis的事务是否符合ACID呢?

原子性 Atomicity

在这里插入图片描述

事务具有原子性是指,数据库将事务中的多个操作当作一个整体来执行,要么全部执行,要么全部不执行。对于Redis的事务功能来说,事务队列中的命令要么就都全部都执行,要么就一个都不执行,因此Redis的事务是具有原子性的。 比如以下成功执行的事务,事务中的所有命令都被执行:

redis> MULTI
OK
redis(TX)> SET fanone 1
QUEUED
redis(TX)> GET fanone
QUEUED
redis(TX)> EXEC
1) OK
2) "1"

与此相反,如果其中有一个命令是错误的,那么整个命令就不会执行。fanone这个key是一个string,并不是set,所以不能使用SADD命令执行。

redis> MULTI
OK
redis(TX)> SET fanone 1
QUEUED
redis(TX)> SADD fanone 2
QUEUED
redis(TX)> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value

那么大家会发现Redis的事务和传统的关系型数据库事务的最大区别在于,Redis不支持事务回滚机制,也就是rollback。

Redis的作者在事务功能的文档中也解释道,不支持事务回滚是因为这种复杂的功能和Redis追求简单高效的设计主旨不相符。

在这里插入图片描述

一致性 Consistency

事务具有一致性指的是,如果数据库在执行事务之前是一致的,那么在事务执行之后,无论事务是否执行成功,数据库也应该依然是一致的。 而这个一致指的是 数据符合数据库本身的定义和要求,没有包含非法或者无效的错误数据

比如执行完事务不会多一个之前没有的命令,或者某个key是string类型,不会变成set类型。我们用上面同一个例子来说明:

redis> MULTI
OK
redis(TX)> SET fanone 1
QUEUED
redis(TX)> SADD fanone 2
QUEUED
redis(TX)> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value

fanone 这个key是string类型,但是无法通过SADD命令将fanone变成set类型。虽然在入队列的时候,redis没有报错,但是在EXEC的时候,redis报了,所以这个key一开始是string类型,事务执行完后也会是string类型,事务执行前后保持了一致。

在这里插入图片描述

隔离性 Isolation

事务的隔离型是指**即使数据库中有多个事务并发的执行,各个事务之间也不会互相影响,并且在并发状态下执行的事务和穿行执行的事务的结果是完成相同的。**

在这里插入图片描述

因为Redis是使用单线程的方式来执行事务以及事务队列中的命令,并且在服务器稳定的情况下,执行事务不会中断,因此,redis的事务总是串行的方式执行的,所以具备隔离性。
在这里插入图片描述

持久性 Durability

事务的持久性值得是当一个事务执行完毕的时候,执行这个事务所得的结果已经被保存到永久性存储介质里面了,即使服务器在事务执行完毕之后停机,执行事务所得的结果也不会丢失。

由于Redis的事务比较简洁,没有提供持久化的能力,所以Redis的事务是依赖于Redis所使用的持久话模式,也就是AOF、RDB,我们一个个来讨论

  • 当服务器无持久化运行的时候,事务不具备持久性,一旦服务器宕机,事务数据将会丢失。

  • 当服务器在RDB持久化模式下运作的时候,服务器只会在特定的保存条件下满足,比如使用BGSAVE命令,队数据库进行保存,但是异步执行的BGSAVE也不能保证,第一时间保存在硬盘中,因此RDB持久化模式下事务不具备持久性
    在这里插入图片描述

  • 而当服务器运行在AOF持久话模式下,并且appendfsync选项是always的时候,服务器总会在执行完命令之后调用同步sync函数,将命令数据保存在硬盘中,而此时事务具备持久性,其他选择比如everysec或者no的时候都不具备持久性。

在这里插入图片描述

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

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

相关文章

LeetCode --- 446 周赛

题目列表 3522. 执行指令后的得分 3523. 非递减数组的最大长度 3524. 求出数组的 X 值 I 3525. 求出数组的 X 值 II 一、执行指令后的得分 照着题目要求进行模拟即可&#xff0c;代码如下 // C class Solution { public:long long calculateScore(vector<string>&…

山东大学软件学院项目实训-基于大模型的模拟面试系统-前端美化滚动条问题

模拟面试界面左侧底部 通过检查工具定位到其所在的位置&#xff1a; 直接对该组件进行美化&#xff1a; <!-- AI面试官列表 --><div class"ai-interviewer-section" v-show"activeTab interviewer"><el-scrollbar class"no-horizont…

git版本回退 | 远程仓库的回退 (附实战Demo)

目录 前言1. 基本知识2. Demo3. 彩蛋 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 爬虫神器&#xff0c;无代码爬取&#xff0c;就来&#xff1a;bright.cn 本身暂存区有多个文件&#xff0c;但手快了&…

什么事Nginx,及使用Nginx部署vue项目(非服务器Nginx压缩包版)

什么是 Nginx? Nginx(发音为 “engine-x”)是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。它以其高性能、高并发处理能力和低资源消耗而闻名。以下是 Nginx 的主要特性和用途: 主要特性 高性能和高并发 Nginx 能够处理大量并发连接,适合高…

第十六周蓝桥杯2025网络安全赛道

因为只会web&#xff0c;其他方向都没碰过&#xff0c;所以只出了4道 做出来的&#xff1a; ezEvtx 找到一个被移动的文件&#xff0c;疑似被入侵 提交flag{confidential.docx}成功解出 flag{confidential.docx} Flowzip 过滤器搜索flag找到flag flag{c6db63e6-6459-4e75-…

高性能的开源网络入侵检测和防御引擎:Suricata介绍

一、Debian下使用Suricata 相较于Windows&#xff0c;Linux环境对Suricata的支持更加完善&#xff0c;操作也更为便捷。 1. 安装 Suricata 在Debian系统上&#xff0c;你可以通过包管理器 apt 轻松安装 Suricata。 更新软件包列表: sudo apt update安装 Suricata: sudo apt …

IP-address-space

导航 (返回顶部) 1. IPv4地址分配表 1.2 IPv4 专用地址注册表1.3 各国IPv4地址分配列表 2. IPv6地址分配表 2.1 IANA IPv6 专用地址注册表2.2 IPv6 多播地址分配 1. IPv4地址分配表1.2 IPv4 专用地址注册表1.3 各国IPv4地址分配列表 2. IPv6地址分配表2.1 IANA IPv6 专用地址…

Ubuntu使用war包部署Jenkins并通过systemcl管理

目录 一、当前系统环境 二、安装Java 二、安装Jenkins 三、使用systemctl管理 一、当前系统环境 操作系统&#xff1a;ubuntu 24.04 Jenkins版本&#xff1a;2.506 格式&#xff1a;war JDK版本&#xff1a;OpenJDK_17 二、安装Java 1.下载jdk安装包 # wget下载 wget …

牛客 verilog入门 VIP

1、输出1 答案&#xff1a; timescale 1ns/1nsmodule top_module(output wire one );assign one 1b1; endmodule 2、wire连线 答案&#xff1a; timescale 1ns/1nsmodule wire0(input wire in0,output wire out1 );assign out1 in0; endmodule 3、多wire连线 timescale 1…

简易版2D我的世界C++程序(有点BUG,但是可以玩!!!)

1、按空格键来切换模式&#xff08;挖掘模式和放置模式&#xff09;&#xff0c;一律用鼠标右键来操作&#xff01;&#xff01;&#xff01; 2、按数字1和2键来切换放置的方块&#xff08;1是草&#xff0c;2是木&#xff09;&#xff0c;树叶不能放置&#xff01;&#xff01…

ubuntu使用dify源码安装部署教程+避坑指南

很多人,包括我在最初使用dify的时候都习惯使用docker来部署安装环境,但在二次开发使用过程中,我们可能希望使用源码来安装,那么这篇文章我将给大家分享如何在ubuntu系统下使用源码安装,并提供大家遇到的疑难杂症如下: dify安装使用过程中报错:/console/api/workspaces/…

java知识体系结构导航

很全&#xff1a;java知识体系结构 个人笔记链接 开发工具IDEA IDEA 插件推荐清单 IDEA快捷键大全 Java基础难点 基础知识_java动态代理 基础知识_java反射机制 基础知识-java流steam 基础知识-java集合collection Spring 01.Spring 框架的演化&#xff1a;从 XML 配置到…

RabbitMQ 的专业术语

术语定义示例/说明生产者&#xff08;Producer&#xff09;发送消息到 RabbitMQ 的客户端应用程序。日志系统将错误信息发送到 RabbitMQ。消费者&#xff08;Consumer&#xff09;从 RabbitMQ 队列中接收并处理消息的客户端应用程序。一个订单处理服务从队列中读取消息并更新数…

mac安装vm虚拟机安装包

因为mac安装虚拟机时&#xff0c;发现下载过程变得不太一样&#xff0c;会比较麻烦。所以决定发一下我已经下载的安装包&#xff0c;个人用户使用免费&#xff0c;商业版请自行去官网下载&#xff01; 百度网盘下载链接 百度网盘 请输入提取码 提取码:d4rc

LLama Factory从入门到放弃

目录 简介 安装 LLama Factory界面介绍 数据格式要求 微调训练 今天在这里介绍一种常用的大模型微调框架——LLama Factory。 简介 LLama Factory 是一个高效的界面化大语言模型微调工具库&#xff0c;支持多种参数高效微调技术&#xff0c;提供简洁接口和丰富示例&#…

如何借助全球动态IP实现多平台账号的批量注册?

无论是社交网络、在线购物平台还是专业应用软件&#xff0c;账号的创建和使用都是必不可少的。然而&#xff0c;在面对不同平台各自的注册限制和策略时&#xff0c;如何高效、安全且合法地进行账号批量注册成为了亟待解决的问题。本文将探讨全球动态IP在这一过程中的作用及其如…

django admin 添加自定义页面

在Django中&#xff0c;你可以通过多种方式向Django Admin添加自定义页面。以下是一些常见的方法&#xff1a; 方法1&#xff1a;使用ModelAdmin的get_urls()方法 如果你只是想添加一个简单的页面来展示信息&#xff0c;你可以在你的ModelAdmin类中重写get_urls()方法。 from…

Docker容器持久化

引言 Docker 容器作为一种轻量级、可移植的虚拟化技术&#xff0c;广泛应用于开发、测试和生产环境中。然而&#xff0c;容器天生是短暂的&#xff0c;意味着它们在生命周期结束后会被销毁&#xff0c;而其中的数据也会随之丢失。为了确保容器中的数据能够持久化&#xff0c;我…

ShaderToy学习笔记 02.圆

1. 画圆 1.1. 圆的方程 圆的方程是&#xff1a;(x^2 y^2 r^2)&#xff0c;其中(r)是圆的半径。 我们可以使用 desmos 来验证一下。 输入 x^2 y^2 -10&#xff0c;即可得到圆。 类似下图 1.2. 画圆的方式 画圆&#xff1a;使用圆的方程&#xff0c;判断每个像素点是否在圆…

一文详解卷积神经网络中的卷积层和池化层原理 !!

文章目录 前言 一、卷积核大小&#xff08;Kernel Size&#xff09; 1. 卷积核大小的作用 2. 常见的卷积核大小 3. 选择卷积核大小的原则 二、步长&#xff08;Stride&#xff09; 1. Stride的作用 三、填充&#xff08;Padding&#xff09; 1. 填充的作用 四、通道数&#xff…