MongoDB 中的事务

前言

在 MongoDB 中,对单个文档的操作都是原子的。因为可以在单个文档结构中使用内嵌文档和数据获得数据之间的关系,所以不必跨多个文档和集合进行范式化,这种结构特性,避免了很多场景中的对多文档事务的需求。

对于需要多个文档进行原子读写的场景,MongoDB 中引入了多文档事务和分布式事务。

  • 在4.0版本中,MongoDB支持副本集上的多文档事务;

  • 在4.2版本中,MongoDB 引入了分布式事务,增加了对分片集群上多文档事务的支持,并合并了对副本集上多文档事务的现有支持,事务可以跨多个操作、集合、数据库、文档和分片使用,这种方式事务的实现主要是借助于两阶段提交协议(2PC)实现的。

如何使用

MongoDB 中从 4.0 开始支持了事务,这里来看看 MongoDB 中的事务是如何使用的呢?

首先登陆 MongoDB 命令行

mongo -u <name> --port 27017 --host 127.0.0.1 admin  -p <pass>

1、打开 session

session = db.getMongo().startSession( { readPreference: { mode: "primary" } } );

2、将需要操作的 collection 进行变量绑定

testCollection = session.getDatabase("gleeman").test_explain;
test1Collection = session.getDatabase("gleeman").test_explain_1;

3、开始事务标注,指定MVCC的模式,写模式

session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );

4、拼接执行语句,将需要执行的语句进行事务封装

try (ClientSession clientSession = client.startSession()) {clientSession.startTransaction();collection.insertOne(clientSession, docOne);collection.insertOne(clientSession, docTwo);clientSession.commitTransaction();
}

5、提交事务

session.commitTransaction();

6、关闭session

session.endSession();

事务的原理

MongoDB 中的 WiredTiger 存储引擎是目前使用最广泛的,这里主要介绍下 WiredTiger 中事务的实现原理。

WiredTiger 存储引擎支持 read-uncommitted 、read-committed 和 snapshot 3 种事务隔离级别,MongoDB 启动时默认选择 snapshot 隔离。

事务和复复制集以及存储引擎之间的关系

1、事务和复制集

复制集配置下,MongoDB 整个事务在提交时,会记录一条 oplog,包含了事务所有的操作,备节点拉取 oplog,并在本地重放事务操作。事务 oplog 包含了事务操作的 lsid,txnNumber,以及事务内所有的操作日志( applyOps 字段)。

WiredTiger 是如何实现事务和 ACID 呢。WiredTiger 事务主要使用了三个技术 snapshot(事务快照)、MVCC (多版本并发控制)和 redo log(重做日志)。同时为了实现这三个技术,还定义了一个基于这三个技术的事务对象和全局事务管理器。

wt_transaction{transaction_id:    // 本次事务的全局唯一的ID,用于标示事务修改数据的版本号snapshot_object:   // 当前事务开始或者操作时刻其他正在执行且并未提交的事务集合,用于事务隔离operation_array:   // 本次事务中已执行的操作列表,用于事务回滚。redo_log_buf:      // 操作日志缓冲区。用于事务提交后的持久化State:             // 事务当前状态
}

WiredTiger 中的 MVCC 是基于 key/value 中 value 值的链表,每个链表单元中存储有当先版本操作的事务 ID 和操作修改后的值。

wt_mvcc{transaction_id:    // 本次修改事务的IDvalue:             // 本次修改后的值
}

WiredTiger 中数据修改都是在这个链表中进行 append 操作,每次对值的修改都是 append 到链表头,每次读取值的时候读是从链表头根据对应的修改事务 transaction_id 和本次事务的 snapshot 来判断是否可读,如果不可读,向链表尾方向移动,直到找到都事务可以读到的数据版本。

什么是 snapshot 呢?

事务开始或者结束操作之前都会对整个 WiredTiger 引擎内部正在执行的或者将要执行的事务进行一次快照,保存当时整个引擎的事务状态,确定那些事务是对自己可见的,哪些事务是自己不可见的。

WiredTiger 中的事务隔离级别

传统的事务级别都分成下面四种:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )。

  • 读未提交:一个事务还没提交时,它的变更就能被别的事务看到,读取未提交的数据也叫做脏读;

  • 读提交:一个事务提交之后,它的变更才能被其他的事务看到;

  • 可重复读:一个事务执行的过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的,在此隔离级别下,未提交的变更对其它事务也是不可见的,此隔离级别基本上避免了幻读;

  • 串行化:这是事务的最高级别,顾名思义就是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

我们熟知的 MySQL 对于事务隔离级别的实现,可重复读 和 读提交 主要是通过 MVCC 来实现,MVCC 的实现主要用到了 undo log 日志版本链和 Read View。串行化 和 读未提交,主要实现方式是通过加锁来实现的。

其中 Read View 可以在理解为一个数据的快照,可重复读隔离级别会在每次启动的事务的时候生成一个 Read View 记录下当前事务启动瞬间,当前所有活跃的事务 ID。具体细节可参见 MySQL中的事务的隔离级别

WiredTiger 存储引擎支持 read-uncommitted、read-committed 和 snapshot 3种事务隔离级别,MongoDB 启动时默认选择 snapshot 隔离。

  • Read-Uncommited:读未提交,一个事务还没提交时,它的变更就能被别的事务看到,读取未提交的数据也叫做脏读,WiredTiger 引擎在实现这个隔方式时,就是将事务对象中的 snap_object.snap_array 置为空即可,那么在读取 MVCC list 中的版本值时,总是读取到 MVCC list 链表头上的第一个版本数据,这样就总是能读取到最新的数据了;

  • read-committed:一个事务提交之后,它的变更才能被其他的事务看到;这种对于一个长事务可能存在多次读取,读取到的值不一样,因为每次读取都是读取的最新提交的数据,WiredTiger 引擎在实现该事务隔离级别,就是在事务在每次执行之前,都对系统机型一次快照,然后在这个事务快照中读取最新提交的数据;

  • snapshot:快照隔离方式,一个事务开始时,就进行一次快照,并且只会进行一次快照,这样事务看到的值提交版本,这个值在整个事务过程中看到的都是一样;

WiredTiger 中对于事务的实现也是基于 MVCC 实现的,MVCC 可以提供基于某个时间点的快照,有了这个快照,就能确定当前事务能看到的数据了,通过这个来实现对应的事务隔离级别,这点也个人感觉和 mysql 中的 Read View 类似,不展开分析了。

WiredTiger 没有使用传统的事务独占锁和共享访问锁来保证事务隔离,而是通过对系统中写事务的 snapshot 截屏来实现。这样做的目的是在保证事务隔离的情况下又能提高系统事务并发的能力。

WiredTiger 事务过程

一般事务有三个阶段:开启事务,执行事务,提交事务。如果事务执行失败,会进行事务的回滚操作,事务正常执行,最近进行事务的提交 (commit) 即可。

事务开启

事务开启的过程中,首先会为事务创建一个事务对象并把这个对象加入到全局的事务管理器当中,然后根据配置确定事务的隔离级别和 redo_log 的刷盘方式,并将事务状态设置成执行状态,最后判断事务的隔离级别,如果是 snapshot 级的事务隔离,在本次事务执行之前会创建一个系统并发事务的 snapshot 截屏,,保存当时整个引擎的事务状态,确定那些事务是对自己可见的,哪些事务是自己不可见的。

事务执行

事务在执行阶段,如果是读操作,不做任何处理,因为读操作不需要回滚和提交。如果是写操作,WiredTiger 会对每个操作做详细的记录。

这里就会用使用到上面介绍的事务对象(wt_transaction)中的 operation_array 和 redo_log_buf。

operation_array:主要记录本次事务中已经提交的操作列表,数组单元中,会包含一个指向 MVCC list 对应修改版本值的指针,用于事务的回滚。

redo_log_buf: 操作日志缓冲区。用于事务提交后的持久化。

来描述下具体的更新操作过程:

1、创建一个 mvcclist 的值对象 update;

2、根据事务对象的 transaction_id 和事务状态判断是为本次事务创建写的事务id,如果没有,为本次事务分配一个事务id,并将事务的状态设置成 HAS_TXN_ID 状态;

3、将本次事务的 ID 设置到 update 单元中作为 mvcc 版本号;

4、同时会创建一个 operation 对象,这个对象的指针会指向 update,这个对象会加入到 operation_array 中,用来进行操作事务的回滚;

5、update 会被加入到 mvcclist 的头部;

6、最后会写一条 redo_log 到本次事务的 redo_log_buffer 当中。

事务提交

提交事务对象中的 redo_log_buf 中的数据到 redo_log_file(重做日志中),并将 redo_log_file 持久化到磁盘上,清除提交事务对象的 snapshot,再将事务对象的transaction_id 设置成 WT_TNX_NODE,保证其他事务在创建 snapshot 时本次事务的状态是已提交的状态。

事务回滚

WiredTiger 引擎对事务的回滚过程比较简单,首先遍历 operation_array ,对每个数组单元对应的 update 事务 id 设置一个 WT_TXN_ABORTED ,标识 mvcc 对应的事务单元被回滚,在其它事务进行 mvcc 读操作的时候,跳过这个放弃的值即可,整个过程是一个无锁的操作,高效,简洁。

事务日志(journal)

Journal 是一种 WAL(Write Ahead Log)事务日志,目的是实现事务提交层面的数据持久化。

Journal 是 MongoDB 存储引擎层面的概念,MongoDB 主要支持的 mmapv1、wiredtiger、mongorocks 等存储引擎,都⽀持配置 JournalMongoDB 可以基于 Journal 来恢复因为崩溃未及时写到磁盘的信息。

Journal 持久化的对象不是修改的数据,而是修改的动作,以日志形式先保存到事务日志缓存中,再根据相应的配置按一定的周期,将缓存中的日志数据写入日志文件中。

事务日志落盘的规则如下。

  • 1、按时间周期落盘。

在默认情况下,以50毫秒为周期,将内存中的事务日志同步到磁盘中的日志文件。

  • 2、提交写操作时强制同步落盘。

当设置写操作的写关注为j:true时,强制将此写操作的事务日志同步到磁盘中的日志文件。

  • 3、事务日志文件的大小达到100MB。

总结

1、在4.0版本中,MongoDB支持副本集上的多文档事务;

2、在4.2版本中,MongoDB 引入了分布式事务,增加了对分片集群上多文档事务的支持,并合并了对副本集上多文档事务的现有支持,事务可以跨多个操作、集合、数据库、文档和分片使用;

3、MongoDB 中的 WiredTiger 存储引擎是目前使用最广泛的,WiredTiger 存储引擎支持 read-uncommitted 、read-committed 和 snapshot 3 种事务隔离级别,MongoDB 启动时默认选择 snapshot 隔离;

  • Read-Uncommited:读未提交,一个事务还没提交时,它的变更就能被别的事务看到,读取未提交的数据也叫做脏读,WiredTiger 引擎在实现这个隔方式时,就是将事务对象中的 snap_object.snap_array 置为空即可,那么在读取 MVCC list 中的版本值时,总是读取到 MVCC list 链表头上的第一个版本数据,这样就总是能读取到最新的数据了;

  • read-committed:一个事务提交之后,它的变更才能被其他的事务看到;这种对于一个长事务可能存在多次读取,读取到的值不一样,因为每次读取都是读取的最新提交的数据,WiredTiger 引擎在实现该事务隔离级别,就是在事务在每次执行之前,都对系统机型一次快照,然后在这个事务快照中读取最新提交的数据;

  • snapshot:快照隔离方式,一个事务开始时,就进行一次快照,并且只会进行一次快照,这样事务看到的值提交版本,这个值在整个事务过程中看到的都是一样;

4、WiredTiger 中对于事务的实现也是基于 MVCC 实现的,MVCC 可以提供基于某个时间点的快照,有了这个快照,就能确定当前事务能看到的数据了,通过这个来实现对应的事务隔离级别,这点也个人感觉和 mysql 中的 Read View 类似,不展开分析了;

5、WiredTiger 没有使用传统的事务独占锁和共享访问锁来保证事务隔离,而是通过对系统中写事务的 snapshot 截屏来实现。这样做的目的是在保证事务隔离的情况下又能提高系统事务并发的能力。

参考

【MongoDB事务】MongoDB事务 - MongoDB-CN-Manual
【WiredTiger的事务实现详解 】WiredTiger的事务实现详解_wiredtiger 事务-CSDN博客
【MongoDB中并发控制】MongoDB中并发控制(MVCC)_mongo的mvcc-CSDN博客

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

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

相关文章

C# .Net Framework Swagger

1.安装 Swagger 在NuGet程序包中安装以下文件 Swashbuckle: Swagger&#xff1a; Swagger.Net: 2.在项目APP_Start 文件夹下面找到 SwaggerNet.cs文件 1.注释掉这两行代码 2.将PreStart方法的内容修改为以下 public static void PreStart() {RouteTable.Routes.MapHttpRoute(…

React18-完成弹窗封装

弹框封装 用法 // 创建 userRef.current?.open(create) // 修改 userRef.current?.open(edit,values){/* 创建用户 */} <CreateUser mRef{userRef} update{} />组件暴露open方法 文档地址&#xff1a;https://react.dev/reference/react/useImperativeHandle useIm…

iview DatePicker 日期选择组件在弹窗中使用transfer,导致选择日期弹窗会关闭的问题

背景&#xff1a;在弹窗里面使用日期选择组件&#xff0c;选择组件的面板被弹窗遮挡了部分&#xff0c;所以需要使用transfer属性&#xff0c;但是使用之后组件面板插入body中了&#xff0c;面板的事件会导致弹窗关闭。 解决方案&#xff1a; 添加上transfer属性和指定的date-…

JSON 方法

JSON 方法 JSON 数据格式规范JSON.stringify使用 replacer格式化&#xff1a;space自定义 toJSON JSON.parse使用 reviver 手写一个简易的 JSON.stringifyeval 实现 JSON.parse总结参考 JSON&#xff08;JavaScript Object Notation&#xff09;是表示值和对象的通用格式&#…

N65总账凭证管理凭证查询(sql)

--核算账簿 select code , name , pk_setofbook from org_setofbook where ( pk_setofbook in ( select pk_setofbook from org_accountingbook where 1 1 and ( pk_group N0001A11000000000037X ) and ( accountenablestate 2 ) ) ) order by code;--核算账簿 select code …

AI在数模中的应用(附2024年美赛AI规则解读)

近期一直有人私信询问AI对数模的应用。本次想借着2024年美赛对AI工具的使用说明已经去年国赛开会的结果&#xff0c;跟大家分享一下国赛、美赛对于AI的态度以及如何使用AI应用于数模。本文将基于本人常用的三种AI工具(ChatGPT、文心一言、NEW bing)进行讲解 根据2023年国赛总结…

armbian docker 部署 homeassistant 忘记密码重置密码

docker run -d \ --name homeassistant \ --privileged \ --restartunless-stopped \ -e TZMY_TIME_ZONE \ -v /etc/docker/ha:/config \ -v /run/dbus:/run/dbus:ro \ -p 8123:8123 \ ghcr.io/home-assistant/home-assistant:stable重置用户的密码&#xff0c;通过容器命令行 …

数据库技术栈 —— B树与B+树

数据库技术栈 —— B树与B树 一、复习二、MySQL中的B树应用 一、复习 B树是多路平衡查找树的意思 参考文章或视频链接[1] 【王道计算机考研 数据结构】 二、MySQL中的B树应用 这篇文章里的计算题还是讲的不错的。 参考文章或视频链接[1] 《探究MySQL的索引结构选型》

【Python】深度解读Python参数

目录 Python参数种类 Python参数定义 不定参数接收 限定传入参数方式 具体示例 位置参数示例 关键词参数示例 两种方法都支持的形式 参数默认值&#xff08;可选参数&#xff09; 参数默认值定义 引用类型默认值异常行为 参数类型声明 参数类型定义申明 函数返回值…

阿里云推出 3.x Java 探针,解锁应用观测与治理的全新姿势

作者&#xff1a;张铭辉、泮圣伟 前言 随着春节大促即将到来&#xff0c;为了确保线上业务高效稳定地运行&#xff0c;电商企业大多会对旗下关键业务应用进行多轮测试。通过模拟线上较高流量的请求&#xff0c;来观察服务性能的实际表现。以某企业的业务测试报告举例&#xf…

SpringClound项目相关

nacos本机模式非虚拟机启动也可正常连接 nacos中的配置中心相当于在application.yml中的相关配置&#xff0c;转移位置&#xff0c;内容同application.yml完全一样均可。 黑马项目导入后&#xff0c;依赖缺失&#xff1a; 首先尝试maven重新加载&#xff0c;控制台提示传递依…

稀疏场景高性能训练方案演变|京东广告算法架构体系最佳实践

近年来&#xff0c;推荐场域为提升模型的表达能力和计算能力&#xff0c;模型规模和计算复杂度大幅增加&#xff0c;同时&#xff0c;高规格硬件资源为模型迭代、算法优化带来了更大的机遇和挑战。为了应对模型规模和算力升级带来的存储、IO和计算挑战&#xff0c;京东零售广告…

解决WindowServer2022关于EDGE浏览器识自签证书问题

1、证书颁发机构服务打开MMC控制台 添加证书、证书模板 2、复制证书模板

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Gauge组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Gauge组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Gauge组件 数据量规图表组件&#xff0c;用于将数据展示为环形图表。 子组件 无…

Unity_Visual Effect Graph

Unity_Visual Effect Graph Unity可视化特效渲染虽不及Unreal Engine,然也还是吊打一众其他引擎的,粗浅整理一波吧,需要深入研究的点实在是太多了。 按照常规包管理方式安装Visual Effect Graph插件: 安装之后,示例文件夹中自带资源,拖入场景即可: 场景只是资源的显…

SOLIDWORKS Simulation 2024增强新功能

SOLIDWORKS 2024 新功能前瞻| SOLIDWORKS Simulation 功能增强 • 性能增强功能 • 壳体的接合交互 • 网格性能 • 欠约束实体检测 • 增强型轴承接头 • 收敛检查图解 • 去耦合混合自由体模式 • 复制算例时排除网格和结果 • 新增在网格化后及分析完成后自动保存模…

C++语法学习

一、字符串 1.字符与整数的联系--ASCII表 0~9 :48~57 A~Z:65~90 a~z:97~122 字符与数字之间转换: 1.1字符转数字&#xff1a; 字符转数字&#xff1a; char c A;cout << c-A << endl; //输出0cout << (int)c << endl; //输出…

k8s学习-数据管理

在Docker中我们知道&#xff0c;要想实现数据的持久化&#xff08;所谓Docker的数据持久化即数据不随着Container的结束而结束&#xff09;&#xff0c;需要将数据从宿主机挂载到容器中&#xff0c;常用的手段就是Volume数据卷。在K8S中&#xff0c;也提供了存储模型Volume&…

Vue学习笔记14 --自定义hook函数/toRef/provide/inject等

9.自定义hook函数 什么是hook&#xff1f;—— 本质是一个函数&#xff0c;把setup函数中使用的Composition API进行了封装。 类似于vue2.x中的mixin。 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。 10.toRef 作用&#xff1a;创建一个 ref 对象&#xff0c;其…

编程笔记 html5cssjs 069 JavaScrip Undefined数据类型

编程笔记 html5&css&js 069 JavaScrip Undefined数据类型 一、undefined数据类型二、类型运算小结 在JavaScript中&#xff0c;undefined 是一种基本数据类型&#xff0c;它表示一个变量已经声明但未定义&#xff08;即没有赋值&#xff09;或者一个对象属性不存在。 一…