本文实例讲述了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数据库程序设计有所帮助。
支持项 支持数据分片后的跨库事务; 两阶段提交保证操作的原子性和数据的强一致性; 服务宕机重启后,提交/回滚中的事务可自动恢复; 支持同时使用 XA 和非 XA 的连接池。 不支持项 服务宕机后,在其它机器上恢复提交/回滚中的数据。
XAShardingSphereTransactionManager 为A pache ShardingSphere 的分布式事务的 XA 实现类。 它主要负责对多数据源进行管理和适配,并且将相应事务的开启、提交和回滚操作委托给具体的 XA 事务管理器。 开启全局事务 收到接入端的 set autoCommit=0 时,XAShardingSphereTransactionManager 将调用具
两阶段事务提交采用的是 X/OPEN 组织所定义的DTP模型所抽象的 AP(应用程序), TM(事务管理器)和 RM(资源管理器) 概念来保证分布式事务的强一致性。 其中 TM 与 RM 间采用 XA 的协议进行双向通信。 与传统的本地事务相比,XA 事务增加了准备阶段,数据库除了被动接受提交指令外,还可以反向通知调用方事务是否可以被提交。 TM 可以收集所有分支事务的准备结果,并于最后进行原子提
本文向大家介绍PostgreSQL数据库事务实现方法分析,包括了PostgreSQL数据库事务实现方法分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了PostgreSQL数据库事务实现方法。分享给大家供大家参考,具体如下: 事务简介 事务管理器:有限状态机 日志管理器 CLOG:事务的执行结果 XLOG:undo/redo日志 锁管理器:实现并发控制,读阶段采用MVCC,写阶段采用锁控
本文向大家介绍详解Android提交数据到服务器的两种方式四种方法,包括了详解Android提交数据到服务器的两种方式四种方法的使用技巧和注意事项,需要的朋友参考一下 Android应用开发中,会经常要提交数据到服务器和从服务器得到数据,本文主要是给出了利用http协议采用HttpClient方式向服务器提交数据的方法。 代码比较简单,这里不去过多的阐述,直接看代码。 以上就是本文的全部内容,希望
本文向大家介绍Mongodb中MapReduce实现数据聚合方法详解,包括了Mongodb中MapReduce实现数据聚合方法详解的使用技巧和注意事项,需要的朋友参考一下 Mongodb是针对大数据量环境下诞生的用于保存大数据量的非关系型数据库,针对大量的数据,如何进行统计操作至关重要,那么如何从Mongodb中统计一些数据呢? 在Mongodb中,给我们提供了三种用于数据聚合的方式: (1)简单