Redis 实战之事务的实现

事务的实现

  • 事务开始
  • 命令入队
  • 事务队列
  • 执行事务
  • 总结

一个事务从开始到结束通常会经历以下三个阶段:

1、 事务开始;
2、 命令入队;
3、事务执行。

本节接下来的内容将对这三个阶段进行介绍, 说明一个事务从开始到结束的整个过程。

事务开始

MULTI 命令的执行标志着事务的开始:

redis> MULTI
OK

MULTI 命令可以将执行该命令的客户端从非事务状态切换至事务状态, 这一切换是通过在客户端状态的 flags 属性中打开 REDIS_MULTI 标识来完成的, MULTI 命令的实现可以用以下伪代码来表示:

def MULTI():

    # 打开事务标识
    client.flags |= REDIS_MULTI

    # 返回 OK 回复
    replyOK()

命令入队

当一个客户端处于非事务状态时, 这个客户端发送的命令会立即被服务器执行:

redis> SET "name" "Practical Common Lisp"
OK

redis> GET "name"
"Practical Common Lisp"

redis> SET "author" "Peter Seibel"
OK

redis> GET "author"
"Peter Seibel"

与此不同的是, 当一个客户端切换到事务状态之后, 服务器会根据这个客户端发来的不同命令执行不同的操作:

如果客户端发送的命令为 EXECDISCARDWATCHMULTI 四个命令的其中一个, 那么服务器立即执行这个命令。
与此相反, 如果客户端发送的命令是 EXECDISCARDWATCHMULTI 四个命令以外的其他命令, 那么服务器并不立即执行这个命令, 而是将这个命令放入一个事务队列里面, 然后向客户端返回 QUEUED 回复。

服务器判断命令是该入队还是该立即执行的过程可以用流程图 IMAGE_ENQUEUE_OR_EXEC 来描述。
在这里插入图片描述

事务队列

每个Redis 客户端都有自己的事务状态, 这个事务状态保存在客户端状态的 mstate 属性里面:

typedef struct redisClient {

    // ...

    // 事务状态
    multiState mstate;      /* MULTI/EXEC state */

    // ...

} redisClient;

事务状态包含一个事务队列, 以及一个已入队命令的计数器 (也可以说是事务队列的长度):

typedef struct multiState {

    // 事务队列,FIFO 顺序
    multiCmd *commands;

    // 已入队命令计数
    int count;

} multiState;

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

typedef struct multiCmd {

    // 参数
    robj **argv;

    // 参数数量
    int argc;

    // 命令指针
    struct redisCommand *cmd;

} multiCmd;

事务队列以先进先出(FIFO)的方式保存入队的命令: 较先入队的命令会被放到数组的前面, 而较后入队的命令则会被放到数组的后面。

举个例子, 如果客户端执行以下命令:

redis> MULTI
OK

redis> SET "name" "Practical Common Lisp"
QUEUED

redis> GET "name"
QUEUED

redis> SET "author" "Peter Seibel"
QUEUED

redis> GET "author"
QUEUED

那么服务器将为客户端创建图 IMAGE_TRANSACTION_STATE 所示的事务状态:

最先入队的 SET 命令被放在了事务队列的索引 0 位置上。
第二入队的 GET 命令被放在了事务队列的索引 1 位置上。
第三入队的另一个 SET 命令被放在了事务队列的索引 2 位置上。
最后入队的另一个 GET 命令被放在了事务队列的索引 3 位置上。

在这里插入图片描述

执行事务

当一个处于事务状态的客户端向服务器发送 EXEC 命令时, 这个 EXEC 命令将立即被服务器执行: 服务器会遍历这个客户端的事务队列, 执行队列中保存的所有命令, 最后将执行命令所得的结果全部返回给客户端。

举个例子, 对于图 IMAGE_TRANSACTION_STATE 所示的事务队列来说, 服务器首先会执行命令:

SET "name" "Practical Common Lisp"

接着执行命令:

GET "name"

之后执行命令:

SET "author" "Peter Seibel"

再之后执行命令:

GET "author"

最后,服务器会将执行这四个命令所得的回复返回给客户端:

redis> EXEC
1) OK
2) "Practical Common Lisp"
3) OK
4) "Peter Seibel"

EXEC 命令的实现原理可以用以下伪代码来描述:

def EXEC():

    # 创建空白的回复队列
    reply_queue = []

    # 遍历事务队列中的每个项
    # 读取命令的参数,参数的个数,以及要执行的命令
    for argv, argc, cmd in client.mstate.commands:

        # 执行命令,并取得命令的返回值
        reply = execute_command(cmd, argv, argc)

        # 将返回值追加到回复队列末尾
        reply_queue.append(reply)

    # 移除 REDIS_MULTI 标识,让客户端回到非事务状态
    client.flags &= ~REDIS_MULTI

    # 清空客户端的事务状态,包括:
    # 1)清零入队命令计数器
    # 2)释放事务队列
    client.mstate.count = 0
    release_transaction_queue(client.mstate.commands)

    # 将事务的执行结果返回给客户端
    send_reply_to_client(client, reply_queue)

总结

事务提供了一种将多个命令打包, 然后一次性、有序地执行的机制。
多个命令会被入队到事务队列中, 然后按先进先出(FIFO)的顺序执行。
事务在执行过程中不会被中断, 当事务队列中的所有命令都被执行完毕之后, 事务才会结束。
带有 WATCH 命令的事务会将客户端和被监视的键在数据库的 watched_keys 字典中进行关联, 当键被修改时, 程序会将所有监视被修改键的客户端的 REDIS_DIRTY_CAS 标志打开。
只有在客户端的 REDIS_DIRTY_CAS 标志未被打开时, 服务器才会执行客户端提交的事务, 否则的话, 服务器将拒绝执行客户端提交的事务。
Redis 的事务总是保证 ACID 中的原子性、一致性和隔离性, 当服务器运行在 AOF 持久化模式下, 并且 appendfsync 选项的值为always 时, 事务也具有耐久性

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【QVariant类型剖析】

QVariant类型剖析 🌟 官方文档中给出的定义🌟 特性🌸QVariant实战应用🌸项目成果展示 🌟 官方文档中给出的定义 📘Because C forbids unions from including types that have non-default constructors or…

(Java)心得:LeetCode——5.最长回文子串

一、原题 给你一个字符串 s,找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。 示例 1: 输入:s "babad" 输出:"bab" 解释:"aba"…

Java并发编程及并发包中类分析

一、并发编程线程基础: 1.什么是线程: 线程是进程中的一个实体,线程本身是不会独立存在的进程是代码在数据集合上的一 次运行活动 , 是系统进行资源分配和调度的基本单位 线程则是进程的 执行路径, 一 个进程中至少有…

2024年5月10日有感复盘

2024年5月10日有感复盘 时间 今天是一个很美好的一天,原因是很平凡,读书很平凡,玩游戏很平凡,然后生活很平凡,未来可期,听歌很舒服,很喜欢一个人呆在图书馆的感觉,很喜欢发呆&…

串口属性中的BM延时计时器问题

如果使用程序修改则需要修改注册表对应位置如下 第一个示例&#xff08;217&#xff09; 第二个示例&#xff08;219&#xff09; 需要注意的事情是修改前必须点查看串口名称&#xff08;例如上图是com5&#xff09; 程序修改&#xff1a; 有没有办法以编程方式更改USB <…

2024第十六届“中国电机工程学会杯”数学建模A题B题思路分析

文章目录 1 赛题思路2 比赛日期和时间3 竞赛信息4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间&#xff1a;2024…

vuex核心概念-actions

目录 一、概述 二、应用场景 三、使用步骤 三、注意 四、辅助函数-mapActions 一、概述 目标&#xff1a;明确actions的基本语法&#xff0c;处理异步操作。 需求&#xff1a;一秒钟之后&#xff0c;修改state的count成666。 说明&#xff1a;mutations必须是同步的(便于…

geotrust企业通配符证书2990

随着时代的变化&#xff0c;人们获取信息的方式由报纸、书籍变为手机、电脑&#xff0c;因此很多企事业单位用户开始在互联网中创建网站来进行宣传&#xff0c;吸引客户。为了维护网站安全环境&#xff0c;保护客户数据&#xff0c;企事业单位也开始使用SSL数字证书&#xff0c…

spfa检测负权回路C++

1.负权边回路首先dist数组刚开始初始化是0&#xff0c;&#xff08;因为是检测回路&#xff0c;那么无论从哪里开始起点都是一样的&#xff0c;所以一开始就会把所有点都加载到队列中&#xff0c;所以就相当于把dist[所有点] 0&#xff09;&#xff1b;下面就是cnt数组----很多…

LeetCode例题讲解:844.比较含退格的字符串

给定 s 和 t 两个字符串&#xff0c;当它们分别被输入到空白的文本编辑器后&#xff0c;如果两者相等&#xff0c;返回 true 。# 代表退格字符。 注意&#xff1a;如果对空文本输入退格字符&#xff0c;文本继续为空。 示例 1&#xff1a; 输入&#xff1a;s "ab#c&qu…

队列 + 宽搜(BFS)

目录 leetcode题目 一、二叉树的层序遍历 二、二叉树的层序遍历 II 三、N叉树的层序遍历 四、二叉树的锯齿形层序遍历 五、二叉树最大宽度 六、在每个树行中找最大值 七、二叉树的层平均值 八、最大层内元素和 九、二叉树的第K大层和 十、反转二叉树的奇数层 leetco…

P8803 [蓝桥杯 2022 国 B] 费用报销

P8803 [蓝桥杯 2022 国 B] 费用报销 分析 最值问题——DP 题意分析&#xff1a;从N张票据中选&#xff0c;且总价值不超过M的票据的最大价值&#xff08;背包问题&#xff09; K天限制 一、处理K天限制&#xff1a; 1.对于输入的是月 日的格式&#xff0c;很常用的方式是…

李宏毅-注意力机制详解

原视频链接&#xff1a;attention 一. 基本问题分析 1. 模型的input 无论是预测视频观看人数还是图像处理&#xff0c;输入都可以看作是一个向量&#xff0c;输出是一个数值或类别。然而&#xff0c;若输入是一系列向量&#xff0c;长度可能会不同&#xff0c;例如把句子里的…

【CTF Web】XCTF GFSJ0477 backup Writeup(备份文件+源码泄漏+目录扫描)

backup X老师忘记删除备份文件&#xff0c;他派小宁同学去把备份文件找出来,一起来帮小宁同学吧&#xff01; 解法 使用 dirsearch 扫描目录。 dirsearch -u http://61.147.171.105:49361/下载&#xff1a; http://61.147.171.105:64289/index.php.bak打开 index.php.bak&am…

一文读懂 RAG:它将如何重新定义 AI 的未来?

RAG 可以使 LLM 能够在实时请求提供事实信息时&#xff0c;访问外部来源的数据&#xff0c;比如经过审核的数据库或互联网上的信息。这样一来&#xff0c;RAG 就消除了大家对于 LLM 仅依赖其训练数据中获得的内部知识库的顾虑&#xff0c;毕竟&#xff0c;这些知识库可能存在缺…

论文研读 Disentangled Information Bottleneck

解耦信息瓶颈 摘要&#xff1a; 信息瓶颈方法是一种从源随机变量中提取与预测目标随机变量相关的信息的技术&#xff0c;通常通过优化平衡压缩和预测项的IB拉格朗日乘子f来实现&#xff0c;然而拉格朗日乘子很难优化&#xff0c;需要多次实验来调整拉格朗日乘子的值&#xff0c…

【TS】入门

创建项目 vscode自动编译ts 生成配置文件 tsc --init 然后发现终端也改变了&#xff1a;

飞跨电容型的三电平(FC-NPC)逆变器simulink仿真模型

本人搭建了飞跨电容型的三电平逆变器simulink仿真模型&#xff0c;相较于二极管钳位型三电平逆变器而言&#xff0c;钳位二极管变为飞跨的电容。采用SPWM调制和均流均压控制&#xff0c;通过搭建仿真模型得到三电平波形。 三电平拓扑中的飞跨电容是指在电路的输出端使用电容来实…

创建一个即时打印XML报表

即时打印的XML报表不需要创建PLSQL程序包,功能顾问良师益友,写个简单的XML报表还是可以的。 其步骤大致分为如下: 创建XML文档。 创建RTF模板。 创建数据源和上传RTF模板。 创建请求并添加到你需要的请求组。 以下具体说明: 创建XML文档,其包括如下部分: 分别是参数、触…

付费文章合集第二期

☞☞付费文章合集第一期 感谢大家一年来的陪伴与支持&#xff01; 对于感兴趣的文章点标题能跳转原文阅读啦~~ 21、Matlab信号处理——基于LSB和DCB音频水印嵌入提取算法 22、CV小目标识别——AITOD数据集&#xff08;已处理&#xff09; 23、Matlab信号发生器——三角波、…