千家信息网

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

发表于:2025-01-21 作者:千家信息网编辑
千家信息网最后更新 2025年01月21日,本文实例讲述了MongoDB数据库两阶段提交实现事务的方法。分享给大家供大家参考,具体如下:MongoDB数据库中操作单个文档总是原子性的,然而,涉及多个文档的操作,通常被作为一个"事务",而不是原子
千家信息网最后更新 2025年01月21日MongoDB数据库两阶段提交实现事务的方法详解

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

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

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

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

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

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

db.accounts.find()

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

{ "_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()

db.transactions.find()

这个操作会返回一个类似下面的文档:
复制代码 代码如下:{ "_id" : ObjectId("4d7bc7a8b8a04f5126961522"), "source" : "A", "destination" : "B", "value" : 100, "state" : "initial" }

切换事务到Pending状态:

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

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

变量t创建后,shell将返回它的值,将会看到如下的输出:
复制代码 代码如下:{ "_id" : ObjectId("4d7bc7a8b8a04f5126961522"), "source" : "A", "destination" : "B", "value" : 100, "state" : "initial" }

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

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:

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集合的内容,现在应该类似于下面的内容:

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

设置事务状态为committed:

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

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事务:

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集合内容,现在应该类似下面内容:

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

设置事务状态为done:

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

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()操作:

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

撤销事务:

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

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集合的内容,应该类似下面:

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

设置事务状态为canceled:

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

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

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

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

事务 状态 文档 面的 账户 代码 应用 数据 两个 内容 数据库 方法 原子 字段 程序 阶段 一致 应用程序 情况 切换 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 sql数据库三组数据分组 数据库单个查询的语句 count 数据库 体现软件开发质量的指标 魔兽世界服务器转移问题 网络安全大赛冠军队员 银行的服务器大概多少钱 网络安全防范个人查摆 万得数据库修改密码 昆明软件开发公司招聘待遇 河南中天浩然网络技术有限公司 山西省政府网络安全讲座 网络安全法对一级响应有何规定 全国应急灾害信息员数据库 如何登录远程服务器下载文件 辽宁网络软件开发互惠互利 监听器消费数据库 华为软件开发有什么要求 uuid是什么类型的数据库 学校网络安全简报百度文库 华3服务器配置管理卡ip 对于服务器的远程管理有哪些 三国杀链接服务器超时 网络安全行为管理系统 万樱网络技术有限公司 数据库录入是否的数据类型 网络安全意识英文作文 计算机网络技术征兵宣传 网络安全意识形态培训 霄龙服务器cpu玩游戏怎么样
0