事务功能使用及原理介绍(一)|学习笔记

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介: 快速学习事务功能使用及原理介绍

开发者学堂课程【MongoDB 快速入门:事务功能使用及原理介绍】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址https://developer.aliyun.com/learning/course/49/detail/1005


事务功能使用及原理介绍(一)


内容介绍:

一、事务的概念

二、事务的使用

三、事务的原理

四、总结


一、事务的概念

事务都具有ACID的特性,A原子性、C一致性、I隔离性、D持久性。原子性是工作单位中不可分割的单位,事务中的操作要么全部执行,要么全部不执行;一致性是指事务不能破坏业务逻辑上的一致性,数据库的状态总是从一个一致性的状态切换到另一个一致性状态;隔离性是指事务在并发时之间是互相隔离的,一个事务不会影响另一个事务的运行状态,就需要对多种事务级别回应级别,包括relongcommited,commited,repeat-for-read,sam-short,sarelizable;持久性是指在事务提交之后,事务的写操作会在数据库中保留并且不会进行数据回滚。

图片18.png

之前的事务主要是将单机的,之后会延伸到分布市场下。

1.为什么MongoDB需要事务

文档模型:使用嵌入文档将不同数据放在一个文档中,如下图左侧中的文档信息所示。

图片19.png

它是一个职场人的信息记录,包括人的名字、职位以及保险身份证号;还有地址,地址里面就是一个简单的嵌入文档,包括城市街道以及邮政编码。文档模型就是把多个相关数据放到一个文本中,这样业务使用中可以在修改文档的时候只用支持单行输入就可以实现对一个文档中的不同字段进行修改,但是在有些场景下,单行事务是不能满足业务需求的。

图片20.png

比如大量多对多的关系中,无法存入一个文档当中,就需要进行拆解。常见的就是股票价格和交易详情,每个交易都会影响到股票的价格变动,这就需要每写一条交易详情就需要同时并发更改投资价格,这个俩个操作需要在一个事务中原子的进行。交易数据详情过多有时无法被放置在一个文档当中,所以这个场景需要跨行的操作。第二个场景就是在新建用户的一个事件,创建用户时需要往应用表中写几条记录,同时也要在事务表中写几条记录,这样系统就可以处理其他应用新建用户的事件。第三个是一个业务操作的场景,比如一个信息需要一个数据操作,同时在一个日志表里记录数据操作,那么这个业务数据操作记录是要在一个事务当中,但操作记录也是要放在一个独立的表里,这两个操作是要跨表进行,并且保持原子性。这三个操作往往是单行输入无法满足的,所以就需要一个跨行事务来进行解决,这也是MongoDB需要进行跨行事务功能的原因。

2.MongoDB对事务的支持

单行事务:原子更新一个文档中的多个字段

副本集的跨行事务(v4.0):在副本集上,跨多个文档,多个表,多个db的多个操作保持事务的原子性

集群的跨行事务(v4.2):在分片集群上,跨多个文档,多个表,多个db的多个操作保持原子性

事务包含部分ddl(v4.4):包括创建表和索引

图片21.png

二、事务的使用

1.MongoDB单行事务

MongoDB进行单行事务操作的时候,并不是对一个数据一个文档进行更新,实际上需要对多个文档进行更新的。比如这个单行事务是对people表进行记录,非唯一索引是对名字进行操作,唯一索引是身份证号。写一个文档需要写入到一个数据表里,如下表的第一行,key是1,代表第一行记录,value是写的整个记录的详情,MongoDB在里面有一个在id的字段;第二行是将索引线写入一个id中,所以这个index_id表里也要有索引记录,key就是一个-id,value就是一个数据表里行的记录,还要把索引线写进用户创建的对应的索引记录中,就是用户创建两个索引,所以要创建一个非唯一索引,一个唯一索引。首先要先创建一个唯一索引ssn身份id,和一个数据表的行号记录,还要写一个非唯一索引,它会出现多个,就不仅要把非唯一索引的包含的数据放进去,还要把对应的行号放进去,这是由于可能会出现多条相同的,要保证key的不一致,它的value也是对应的行号,第四个操作是要把唯一的索引检查key是否重复,从ssn索引记录表中看是否已经存在,存在就会有报错的操作,最后还要把操作的记录写入到复制表里,以便于把写操作同步在second节点上。所以单行的写操作是要在底层写了5条记录,所以说它的单行操作是需要存储引擎支持一个单行事务的,这个多操作放在一个事务里执行

单行事务操作:db.people.insert({ssn:”579-34-3243”,

first_name:”Nicholas”,last_name:”Turner”,...

})

people表:非唯一索引{first_name:1,last_name:1},唯一索引{ssn:1}

Key Value

1 {_id:”57d7a121fa937f710a7d4877”,ssn:”579-34-3243”,

first_name:”Nicholas”,last_name:”Turner”,...

}

Index_id:57d7a121fa937f710a7d4877 1

Index_ssn:579-34-3243 1

Index_name:Nicholas/Turner/1 1

oplog.rs:1607662217 {“ts”:Timetamp(1607662217,1),”op”:”i”,”ns”:”test.people”,”o”:{_id:”57d7a121fa937f710a7d4877”,ssn:”579-34-3243”,

first_name:”Nicholas”,last_name:”Turner”,...

},...}

2.MongoDB副本集的跨行事务

在有些业务场景上,单行输入可能不会满足相应的业务需求,就需要去使用跨行输入。而跨行输入是在4.0之后才会支持,下图左侧是副本集的架构图,应用去读写记录,它会写一个记录复制到second 的记录上,事务操作都是读写主库的。

图片22.png

事务参数:readConcern:snapshot,writeConcern:mjority,readPreference:primary

限制:

事务最长生命周期:transactionLifetimeLimitSeconds(60s)这个是由事务设置的,但他不能超过transactionLifetimeLimitSeconds,一般这个数值是60s,即一个事务一般不能超过60s

(v4.0)所有写操作的数据大小不超过16MB,所有写操作都是在一行的,它是由oplog的限制,所以它的大小限制为16MB,但是在4.2版本之后就没有了这样的限制。就可以把这个写操作放在多行记录里面来存储。

跨行事务举例

图片23.png

上述的这个例子和之前的表格是一致的场景,首先开启一个session,在一个session上去获取一个people员工表,之后在获取一个公司表,在session上开启一个事务就是snapshot的majority。之后在员工表里查找一个员工id看信息是否存在。这个设定的场景就是一个员工从一家公司跳入到另一家公司的,把这个操作放到事务里面就是现在公司表里面新建一个新跳入的公司,比如这个公司里面名字和地址,在员工表里面把员工对应的公司id进行一个替换,把这两个操作放在一个事务里面进行事务提交,这是比较常见的跨表事务操作,这个跨行过程中也有一定的限制。

3.MongoDB副本集的跨行事务

生产环境的事务代码、考虑写冲入、网络报错

图片24.png

这个代码是一段样例代码,在写事务的时候也是要注意一些写冲突、网络报错问题场景。这个是一个可以放在实物生产中的一段代码,这段函数分为一段声明函数和函数调用,首先先看下面一部分函数调用的代码。

首先是一个while循环之后是true循环,它先运行了一个事务,使用了一个try-expect语句来捕捉这个事务的报错,这个报错里面会有一个识别的error_label(是标识一类的错误码),当这段的错误码是一个transaction-error的错误时发生的网络报错,这种场景下是遇到了写冲突和请求发生时的网络报错,是可以通过重试来解决的,所以发现这种报错情况就会重试。当不是这种类型报错,它就会抛出异常,比如说是reply_key这种业务逻辑的报错,就会抛出异常重试是没有效果的。接下来看上面一段的代码,它是一个session开启事务,然后在一个表写进入一个记录,在另一个表加入另一个记录。另外commit_transaction是做了while的一个循环,这个也是为了解决一种网络请求报错,但它的报错不是发送网络请求的报错,它是MongoDB回报的时候因为的网络报错,因为它并没有被客户端接收所以它不清楚事务是否提交成功。这个客户端就会产生一个报错是UnknownTransactionCommitResult,不清楚事务提交的结果,这种就是网络报错,即commit时的网络报错,这是的commit也是要加入for循环进行commit重试,就会在下一次得到commit的结果。除了这些报错,其他报错是不需要进行重试的,直接raise就行。在这些报错里面是无限循环的,业务的实际运用当中可以加上业务的重试次数和重试时间的约束限制,避免做重复的重试操作。

4.MongoDB集群的跨行事务(分布式事务)

在4.2版本之后MongoDB实现了集群的跨行分布即分布式事务。

下图左侧是集群的样例图,图中上面两个是Router,下面是两个shard,还有一个ConfigServers。分布式事务就是集群的跨行事务和副本的跨行事务参数是一样的,参数的用法会在后面的学习中详细讲述。

图片25.png

事务参数:readConcern:snapshot,writeConcern:mjority,readPreference:primary

分片表:people(ssn:“hashed”)

这段样例代码和之前的副本集是一段代码,这里面主要不同就是他里面的people表在分片集群里是分片表做哈希分片,使用ssn身份证号作为唯一的标识符字段,但它的写操作都是在shard的primary上进行读写,它的使用方式就是和副本集的事务方式是一样的,好处就是当副本集的事务迁往到集群的时候它业务上的代码不用更改,对于开发者更为友好。

图片26.png

5.MongoDB事务包含部分ddl

在4.4版本之后,是支持部分ddl。但是这个ddl主要是创建表和创建索引,会有两种场景需要使用ddl,比如下图左侧一开始使用的是北京的region里面部署的一个MongoDB数据库,当需要把业务拓展到杭州时,在杭州region里面也扩展这个数据库时,就要在里面部署一个属于杭州的MongoDB数据库;这个时候库的一些初始化操作,比如创建一些表,索引的信息,需要同步一致的传入到杭州的数据库里,这就要保证业务初始化保持原子性或是全都初始化成功或是全部都初始化失败,回滚掉。使用包含部分ddl事务是可以使业务的初始化保持原子性。

图片27.png

第二种就是在事务中的的隐式操作,当它映射到一个创建表时,它也可以创建一个表,这种使用往往是在开发环境测试中,数据是不用关心性能的,对业务的要求不高,可以直接把代码写到一个新库里面(初始的公库里)这样隐式的操作会包含一些创表的操作,但可能不会有索引及一些相关信息,但也能保证业务代码不报错,这个使用体验在开发测试时很好

事务操作包含创建表和索引:

让整个业务库的初始化保持原子性

事务在的insert操作可以隐式创建表

图片28.png

这段代码也是隐式创表的一个样例,例子和前几个是一样,是insert往company表里操作时,company会隐式的创建出一个表,所以说就不会报错,但是在session中的表若是不存在的话就会报错。

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
Java Spring
代码如何实现事务查询
代码如何实现
128 0
|
存储 SQL NoSQL
事务功能使用及原理介绍(二)|学习笔记
快速学习事务功能使用及原理介绍
197 0
事务功能使用及原理介绍(二)|学习笔记
|
存储 缓存 监控
ChangeStreams 使用及原理(二)|学习笔记
快速学习 ChangeStreams 使用及原理
662 0
ChangeStreams 使用及原理(二)|学习笔记
|
SQL 运维 监控
ChangeStreams 使用及原理(一)|学习笔记
快速学习 ChangeStreams 使用及原理
426 0
ChangeStreams 使用及原理(一)|学习笔记
|
存储 SQL 弹性计算
分布式事务与数据分区(二)|学习笔记
快速学习分布式事务与数据分区(二)
分布式事务与数据分区(二)|学习笔记
|
存储 SQL NoSQL
分布式事务与数据分区(一)|学习笔记
快速学习分布式事务与数据分区(一)
分布式事务与数据分区(一)|学习笔记
|
SQL Java Apache
区分多数据源实现(二)|学习笔记
快速学习区分多数据源实现(二)
114 0
区分多数据源实现(二)|学习笔记
|
Web App开发 缓存 负载均衡
缓存基础(一)|学习笔记
快速学习 缓存基础(一)
109 0
缓存基础(一)|学习笔记
|
存储 缓存 边缘计算
缓存基础(二)|学习笔记
快速学习 缓存基础(二)
123 0
缓存基础(二)|学习笔记
|
Go 开发者
for 循环注意事项和细节(1)| 学习笔记
快速学习 for 循环注意事项和细节(1)
for 循环注意事项和细节(1)| 学习笔记