服务器之家:专注于服务器技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - MongoDB - MongoDB数据库两阶段提交实现事务的方法详解

MongoDB数据库两阶段提交实现事务的方法详解

2020-05-18 15:48after_you MongoDB

这篇文章主要介绍了MongoDB数据库两阶段提交实现事务的方法,结合实例形式详细分析了MongoDB数据库事务提交、回滚、撤销等操作的原理、实现方法及相关操作注意事项,需要的朋友可以参考下

本文实例讲述了MongoDB数据库两阶段提交实现事务的方法。分享给大家供大家参考,具体如下:

MongoDB数据库中操作单个文档总是原子性的,然而,涉及多个文档的操作,通常被作为一个“事务”,而不是原子性的。因为文档可以是相当复杂并且包含多个嵌套文档,单文档的原子性对许多实际用例提供了支持。尽管单文档操作是原子性的,在某些情况下,需要多文档事务。在这些情况下,使用两阶段提交,提供这些类型的多文档更新支持。因为文档可以表示为Pending数据和状态,可以使用一个两阶段提交确保数据是一致的,在一个错误的情况下,事务前的状态是可恢复的。

事务最常见的例子是以可靠的方式从A账户转账到B账户,在关系型数据库中,此操作将从A账户减掉金额和给B账户增加金额的操作封装在单个原子事务中。在MongoDB中,可以使用两阶段提交达到相同的效果。本文中的所有示例使用mongo shell与数据库进行交互,并假设有两个集合:首先,一个名为accounts的集合存储每个账户的文档数据,另一个名为transactions的集合存储事务本身。

首先创建两个名为A和B的账户,使用下面的命令:

?
1
2
db.accounts.save({name: "A", balance: 1000, pendingTransactions: []})
db.accounts.save({name: "B", balance: 1000, pendingTransactions: []})

使用find()方法验证这两个操作已经成功:

?
1
db.accounts.find()

mongo会返回两个类似下面的文档:

?
1
2
{ "_id" : ObjectId("4d7bc66cb8a04f512696151f"), "name" : "A", "balance" : 1000, "pendingTransactions" : [ ] }
{ "_id" : ObjectId("4d7bc67bb8a04f5126961520"), "name" : "B", "balance" : 1000, "pendingTransactions" : [ ] }

事务过程:

设置事务初始状态initial:

通过插入下面的文档创建transaction集合,transaction文档持有源(source)和目标(destination),它们引用自accounts集合文档的字段名,以及value字段表示改变balance字段数量的数据。最后,state字段反映事务的当前状态。

复制代码 代码如下:
db.transactions.save({source: "A", destination: "B", value: 100, state: "initial"})

 

验证这个操作已经成功,使用find()

?
1
db.transactions.find()

这个操作会返回一个类似下面的文档:

复制代码 代码如下:
{ "_id" : ObjectId("4d7bc7a8b8a04f5126961522"), "source" : "A", "destination" : "B", "value" : 100, "state" : "initial" }

 

切换事务到Pending状态:

在修改accounts集合记录之前,将事务状态从initial设置为pending。使用findOne()方法将transaction文档赋值给shell会话中的局部变量t:

?
1
t = db.transactions.findOne({state: "initial"})

变量t创建后,shell将返回它的值,将会看到如下的输出:

复制代码 代码如下:
{ "_id" : ObjectId("4d7bc7a8b8a04f5126961522"), "source" : "A", "destination" : "B", "value" : 100, "state" : "initial" }

 

使用update()改变state的值为pending:

?
1
2
db.transactions.update({_id: t._id}, {$set: {state: "pending"}})
db.transactions.find()

find()操作将返回transaction集合的内容,类似下面:

复制代码 代码如下:
{ "_id" : ObjectId("4d7bc7a8b8a04f5126961522"), "source" : "A", "destination" : "B", "value" : 100, "state" : "pending" }

 

将事务应用到两个账户:

使用update()方法应用事务到两个账户。在update()查询中,条件pendingTransactions:{$ne:t._id}阻止事务更新账户,如果账户的pendingTransaction字段包含事务t的_id:

?
1
2
3
4
5
6
7
8
9
db.accounts.update(
 { name: t.source, pendingTransactions: { $ne: t._id } },
 { $inc: { balance: -t.value }, $push: { pendingTransactions: t._id } }
)
db.accounts.update(
 { name: t.destination, pendingTransactions: { $ne: t._id } },
 { $inc: { balance: t.value }, $push: { pendingTransactions: t._id } }
)
db.accounts.find()

find()操作将返回accounts集合的内容,现在应该类似于下面的内容:

?
1
2
{ "_id" : ObjectId("4d7bc97fb8a04f5126961523"), "balance" : 900, "name" : "A", "pendingTransactions" : [ ObjectId("4d7bc7a8b8a04f5126961522") ] }
{ "_id" : ObjectId("4d7bc984b8a04f5126961524"), "balance" : 1100, "name" : "B", "pendingTransactions" : [ ObjectId("4d7bc7a8b8a04f5126961522") ] }

设置事务状态为committed:

使用下面的update()操作设置事务的状态为committed:

?
1
2
db.transactions.update({_id: t._id}, {$set: {state: "committed"}})
db.transactions.find()

find()操作发回transactions集合的内容,现在应该类似下面的内容:

复制代码 代码如下:
{ "_id" : ObjectId("4d7bc7a8b8a04f5126961522"), "destination" : "B", "source" : "A", "state" : "committed", "value" : 100 }

 

移除pending事务:

使用下面的update()操作从accounts集合中移除pending事务:

?
1
2
3
db.accounts.update({name: t.source}, {$pull: {pendingTransactions: t._id}})
db.accounts.update({name: t.destination}, {$pull: {pendingTransactions: t._id}})
db.accounts.find()

find()操作返回accounts集合内容,现在应该类似下面内容:

?
1
2
{ "_id" : ObjectId("4d7bc97fb8a04f5126961523"), "balance" : 900, "name" : "A", "pendingTransactions" : [ ] }
{ "_id" : ObjectId("4d7bc984b8a04f5126961524"), "balance" : 1100, "name" : "B", "pendingTransactions" : [ ] }

设置事务状态为done:

通过设置transaction文档的state为done完成事务:

?
1
2
db.transactions.update({_id: t._id}, {$set: {state: "done"}})
db.transactions.find()

find()操作返回transaction集合的内容,此时应该类似下面:

复制代码 代码如下:
{ "_id" : ObjectId("4d7bc7a8b8a04f5126961522"), "destination" : "B", "source" : "A", "state" : "done", "value" : 100 }

 

从失败场景中恢复:

最重要的部分不是上面的典型例子,而是从各种失败场景中恢复未完成的事务的可能性。这部分将概述可能的失败,并提供方法从这些事件中恢复事务。这里有两种类型的失败:

1、所有发生在第一步(即设置事务的初始状态initial)之后,但在第三步(即应用事务到两个账户)之前的失败。为了还原事务,应用应该获取一个pending状态的transaction列表并且从第二步(即切换事务到pending状态)中恢复。

2、所有发生在第三步之后(即应用事务到两个账户)但在第五步(即设置事务状态为done)之前的失败。为了还原事务,应用需要获取一个committed状态的事务列表,并且从第四步(即移除pending事务)恢复。

因此应用程序总是能够恢复事务,最终达到一个一致的状态。应用程序开始捕获到每个未完成的事务时运行下面的恢复操作。你可能还希望定期运行恢复操作,以确保数据处于一致状态。达成一致状态所需要的时间取决于应用程序需要多长时间恢复每个事务。

回滚:

在某些情况下可能需要“回滚”或“撤消”事务,当应用程序需要“取消”该事务时,或者是因为它永远需要恢复当其中一个帐户不存在的情况下,或停止现有的事务。这里有两种可能的回滚操作:

1、应用事务(即第三步)之后,你已经完全提交事务,你不应该回滚事务。相反,创建一个新的事务,切换源(源)和目标(destination)的值。

2、创建事务(即第一步)之后,在应用事务(即第三步)之前,使用下面的处理过程:

设置事务状态为canceling:

首先设置事务状态为canceling,使用下面的update()操作:

?
1
db.transactions.update({_id: t._id}, {$set: {state: "canceling"}})

撤销事务:

使用下面的操作顺序从两个账户中撤销事务:

?
1
2
3
db.accounts.update({name: t.source, pendingTransactions: t._id}, {$inc: {balance: t.value}, $pull: {pendingTransactions: t._id}})
db.accounts.update({name: t.destination, pendingTransactions: t._id}, {$inc: {balance: -t.value}, $pull: {pendingTransactions: t._id}})
db.accounts.find()

find()操作返回acounts集合的内容,应该类似下面:

?
1
2
{ "_id" : ObjectId("4d7bc97fb8a04f5126961523"), "balance" : 1000, "name" : "A", "pendingTransactions" : [ ] }
{ "_id" : ObjectId("4d7bc984b8a04f5126961524"), "balance" : 1000, "name" : "B", "pendingTransactions" : [ ] }

设置事务状态为canceled:

最后,使用下面的update()状态将事务状态设置为canceled:

?
1
db.transactions.update({_id: t._id}, {$set: {state: "canceled"}})

参考资料:http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/

希望本文所述对大家MongoDB数据库程序设计有所帮助。

原文链接:https://blog.csdn.net/after_you/article/details/68059774

延伸 · 阅读

精彩推荐
  • MongoDBMongoDB多条件模糊查询示例代码

    MongoDB多条件模糊查询示例代码

    这篇文章主要给大家介绍了关于MongoDB多条件模糊查询的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用MongoDB具有一定的参考学习价值...

    浅夏晴空5902020-05-25
  • MongoDB在mac系统下安装与配置mongoDB数据库

    在mac系统下安装与配置mongoDB数据库

    这篇文章主要介绍了在mac系统下安装与配置mongoDB数据库的操作步骤,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    CXYhh1219312021-11-14
  • MongoDBMongodb索引的优化

    Mongodb索引的优化

    MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。接下来通过本文给大家介绍Mongodb索引的优化,本文介绍的非常详细,具有参考借鉴价值,感...

    MRR3252020-05-05
  • MongoDBMongoDB查询之高级操作详解(多条件查询、正则匹配查询等)

    MongoDB查询之高级操作详解(多条件查询、正则匹配查询等)

    这篇文章主要给大家介绍了关于MongoDB查询之高级操作(多条件查询、正则匹配查询等)的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者...

    w田翔3872020-12-19
  • MongoDBMongoDB系列教程(五):mongo语法和mysql语法对比学习

    MongoDB系列教程(五):mongo语法和mysql语法对比学习

    这篇文章主要介绍了MongoDB系列教程(五):mongo语法和mysql语法对比学习,本文对熟悉Mysql数据库的同学来说帮助很大,用对比的方式可以快速学习到MongoDB的命...

    MongoDB教程网3252020-05-01
  • MongoDBWindows下MongoDB配置用户权限实例

    Windows下MongoDB配置用户权限实例

    这篇文章主要介绍了Windows下MongoDB配置用户权限实例,本文实现需要输入用户名、密码才可以访问MongoDB数据库,需要的朋友可以参考下 ...

    MongoDB教程网3082020-04-29
  • MongoDBMongoDB的索引

    MongoDB的索引

    数据库中的索引就是用来提高查询操作的性能,但是会影响插入、更新和删除的效率,因为数据库不仅要执行这些操作,还要负责索引的更新 ...

    MongoDB教程网2532020-05-12
  • MongoDBmongodb数据库基础知识之连表查询

    mongodb数据库基础知识之连表查询

    这篇文章主要给大家介绍了关于mongodb数据库基础知识之连表查询的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用mongodb具有一定的参...

    ZJW02155642020-05-22