文档章节

mongodb使用两阶段提交实现事务

 追逐技术的IT人
发布于 2017/04/01 15:27
字数 1278
阅读 91
收藏 2

1.mongodb实现事务的方法    

mongoDB数据库中操作单个文档总是原子性的,然而,涉及多个文档的操作,通常被作为一个“事务”,而不是原子性的。因为文档可以是相当复杂并且包含多个嵌套文档,单文档的原子性对许多实际用例提供了支持。尽管单文档操作是原子性的,在某些情况下,需要多文档事务。在这些情况下,使用两阶段提交,提供这些类型的多文档更新支持。MongoDB的两阶段提交实际上是通过使用另外一个表存储中间状态控制事务的原子性和一致性。官网解释:https://docs.mongodb.com/manual/tutorial/perform-two-phase-commits/

2.mongodb实现事务的例子

以从A账户转100给B帐户作为例子来看如何通过两阶段提交的方式实现事务

2.1 转账初始化

a.初始化两个帐号A,B:

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

b.使用另外一个表初始化事务状态(包含两个账户_id,转账金额,当前状态,上次修改时间):

db.transactions.insert(
    { _id: 1, source: "A", destination: "B", value: 100, state: "initial", lastModified: new Date() }
)

2.2 开始事务

a.设置事务状态为pending状态并设置修改时间:

var t = db.transactions.findAndModify(
       {
         query: { state: "initial" },
         update:
           {
             $set: { state: "pending"},
             $currentDate: { lastModified: true }
           },
         new: true
       }
    )

如果更新成功,会返回WriteResult对象中的nMatched和nModified的值为1,如果nMatched和nModified是0,重新执行2.1步骤开启一个新的事务

b.更新账户A减少100,加入了查询条件pendingTransactions,通过事务_id避免事务的重复执行

db.accounts.update(
   { _id: t.source, pendingTransactions: { $ne: t._id } },
   { $inc: { balance: -t.value }, $push: { pendingTransactions: t._id } }
)

如果更新成功,会返回WriteResult对象中的nMatched和nModified的值为1

c.更新账户B增加100,加入了查询条件pendingTransactions,通过事务_id避免事务的重复执行

db.accounts.update(
   { _id: t.destination, pendingTransactions: { $ne: t._id } },
   { $inc: { balance: t.value }, $push: { pendingTransactions: t._id } }
)

如果更新成功,会返回WriteResult对象中的nMatched和nModified的值为1

2.3 更新事务已经生效

a.更新事务执行状态为已生效以及时间为当前时间

db.transactions.update(
   { _id: t._id, state: "pending" },
   {
     $set: { state: "applied" },
     $currentDate: { lastModified: true }
   }
)

如果更新成功,会返回WriteResult对象中的nMatched和nModified的值为1

b.更新A账户并删掉依赖的事务_id

db.accounts.update(
   { _id: t.source, pendingTransactions: t._id },
   { $pull: { pendingTransactions: t._id } }
)

c.更新B账户并删掉依赖的事务_id

db.accounts.update(
   { _id: t.destination, pendingTransactions: t._id },
   { $pull: { pendingTransactions: t._id } }
)

2.4 更新事务为已完成

a.更新事务执行状态为已完成以及时间为当前时间

db.transactions.update(
   { _id: t._id, state: "applied" },
   {
     $set: { state: "done" },
     $currentDate: { lastModified: true }
   }
)

如果更新成功,会返回WriteResult对象中的nMatched和nModified的值为1

2.5 事务失败后的恢复

2.5.1 pending阶段失败的恢复

如果在上面的2.2步骤中的事务失败了,当前状态是pending状态,恢复方式是重新执行2.2步骤中的更新用户账户信息步骤,例如每隔三十分钟检查之前还处于pending的事务,重新执行2.2步骤中的更新用户账户信息步骤:

var dateThreshold = new Date();
dateThreshold.setMinutes(dateThreshold.getMinutes() - 30);
var t = db.transactions.findOne( { state: "pending", lastModified: { $lt: dateThreshold } } );

2.5.2 applied阶段失败的恢复

如果在上面2.3步骤中的事务失败了,当前状态是applied状态,恢复方式是重新执行2.3步骤中的删除账户依赖的事务_id,例如每隔三十分钟检查之前还处于pending的事务,重新执行2.3步骤中的删除账户依赖的事务_id:

var dateThreshold = new Date();
dateThreshold.setMinutes(dateThreshold.getMinutes() - 30);
var t = db.transactions.findOne( { state: "applied", lastModified: { $lt: dateThreshold } } );

2.6 事务的回滚

2.6.1 pending阶段的回滚

如果事务处在pending阶段需要回滚,需要执行以下步骤

a.更新事务状态为正在取消以及修改时间:

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

如果更新成功,会返回WriteResult对象中的nMatched和nModified的值为1

b.账户B执行与以前相反的操作,增加pendingTransactions条件的目的是只回滚已经执行过的操作:

db.accounts.update(
   { _id: t.destination, pendingTransactions: t._id },
   {
     $inc: { balance: -t.value },
     $pull: { pendingTransactions: t._id }
   }
)

如果更新成功,会返回WriteResult对象中的nMatched和nModified的值为1

c.账户A执行与以前相反的操作,增加pendingTransactions条件的目的是只回滚已经执行过的操作:

db.accounts.update(
   { _id: t.source, pendingTransactions: t._id },
   {
     $inc: { balance: t.value},
     $pull: { pendingTransactions: t._id }
   }
)

如果更新成功,会返回WriteResult对象中的nMatched和nModified的值为1

d.更新事务执行状态为已取消:

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

如果更新成功,会返回WriteResult对象中的nMatched和nModified的值为1

2.6.2 applied阶段的回滚

如果事务处在applied阶段需要回滚,需要执行以下步骤

a.把当前事务执行结束

b.再开启一个新的事务,把事务执行的源和目标互换,执行一个相反的事务

© 著作权归作者所有

粉丝 4
博文 23
码字总数 33703
作品 0
南京
程序员
私信 提问
MySQL Cluster 与 MongoDB 复制群集分片设计及原理

分布式数据库计算涉及到分布式事务、数据分布、数据收敛计算等等要求 分布式数据库能实现高安全、高性能、高可用等特征,当然也带来了高成本(固定成本及运营成本),我们通过MongoDB及MySQL...

微笑向暖wx
01/26
32
0
MySQL与MongoDB复制群集分片设计及原理

  【IT168 技术】分布式数据库计算涉及到分布式事务、数据分布、数据收敛计算等等要求。   分布式数据库能实现高安全、高性能、高可用等特征,当然也带来了高成本(固定成本及运营成本),...

作者:Yuanyun
2012/04/09
0
0
MongoDB+Mysql 做积分系统

最近项目要做一个积分系统(项目基于Spring框架),刚开始只用MongoDB做,但过程中涉及到多表操作,遇到数据一致性的问题,查资料说可以利用两阶段提交去模拟事务,但代码实现起来有点复杂;...

小鱼吃虾米
2014/11/07
1K
2
重大功能,MongoDB 4.0 将正式支持 ACID 事务

MongoDB CTO Eliot Horowitz 刚刚于2月16日凌晨在MongoDB西雅图大会上宣布,MongoDB将在4.0版本中正式推出多文档ACID事务支持 。 “MongoDB 4.0 将支持多文档事务,届时MongoDB将成为唯一能够...

周其
2018/02/17
13.9K
17
11-【MongoDB入门教程】MongoDB原子性和事务

在MongoDB中,写操作的原子性是在级别上的,即使修改的是文档中的内嵌部分,写锁的级别也是上。 当一个写操作要修改多个文档,每个文档的修改是原子性的。整个的写操作并不是原子性的,它可能...

jockchou
2015/07/03
2.9K
0

没有更多内容

加载失败,请刷新页面

加载更多

nginx学习笔记

中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。 是连接两个独立应用程序或独立系统的软件。 web请求通过中间件可以直接调用操作系统,也可以经过中间件把请求分发到多...

码农实战
今天
5
0
Spring Security 实战干货:玩转自定义登录

1. 前言 前面的关于 Spring Security 相关的文章只是一个预热。为了接下来更好的实战,如果你错过了请从 Spring Security 实战系列 开始。安全访问的第一步就是认证(Authentication),认证...

码农小胖哥
今天
9
0
JAVA 实现雪花算法生成唯一订单号工具类

import lombok.SneakyThrows;import lombok.extern.slf4j.Slf4j;import java.util.Calendar;/** * Default distributed primary key generator. * * <p> * Use snowflake......

huangkejie
昨天
12
0
PhotoShop 色调:RGB/CMYK 颜色模式

一·、 RGB : 三原色:红绿蓝 1.通道:通道中的红绿蓝通道分别对应的是红绿蓝三种原色(RGB)的显示范围 1.差值模式能模拟三种原色叠加之后的效果 2.添加-颜色曲线:调整图像RGB颜色----R色增强...

东方墨天
昨天
11
1
将博客搬至CSDN

将博客搬至CSDN

算法与编程之美
昨天
13
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部