概述

  • Mongodb 4.0 支持副本集的多文档事务
  • Mongodb 4.2 支持分片集群的多文档事务

单个Server是不支持使用事务,所以要学习事务,需要搭建一个副本集/分片集群

另外需要说明是,单个文档操作是原子操作,而mongodb是文档型数据库,在单个文档上,可以嵌入对象/数组这种格式来维护数据的关系,而不应该使用多个集合来维护数据之间的关系。由于mongodb的这种特性,所以单个文档操作消除了很多需要事务的需求。

搭建副本集

下面以最简单的方式搭建一个副本集

1.  启动多个mongod实例,这里使用cmd命令启动

start "Mongodb Service - 27017" /min mongod --port 27017 --replSet "rs0" --dbpath "F:\Database\Mongodb\Data27017" --logpath "F:\Database\Mongodb\Log\mongod.27017.log"
start "Mongodb Service - 27018" /min mongod --port 27018 --replSet "rs0" --dbpath "F:\Database\Mongodb\Data27018" --logpath "F:\Database\Mongodb\Log\mongod.27018.log"

参数说明

  • replSet : 设置副本集名称
  • port : 设置端口,因为我是单机,所以只能设置不同端口
  • dbpath: 数据文件路径,注:文件夹必须是存在,mongod不会自动创建
  • logpath: 日志文件名称,这个不需要提前新建,若不存在mongod会自动创建

2.  连接任意一个实例,这里就选择27017这个默认端口

mongo

3. 启动副本集

rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "127.0.0.1:27017" },
{ _id: 1, host: "127.0.0.1:27018" }
]
})

参数说明

  • _id : 副本集名称,就是启动实例时指定那个名称
  • members : 这个就是所有成员,_id每个成员的标识,整数型[0,255]

返回字段"ok" : 1 代表创建成功

rs.initiate({}),这里除了几个必须的,都是使用默认配置去启动,更多配置参数可以参考replica-configuration

4. 查看当前配置信息

rs.conf()

5. 查看副本集信息

rs.status()

到这,副本集就搭建完成

事务

1. 连接副本集

mongo mongodb://127.0.0.1:27017,127.0.0.1:27018/?replicaSet=rs0

可以直接连接主副本的实例,也可以用这种url形式可以自动连接主副本(推荐使用后者)

2. 准备2条数据

db.balance.insert({ name: "Wilson", balance: 100 }, { writeConcern: { w: "majority", wtimeout: 2000 } });
db.record.insert({ name: "Wilson", change: 100, balance: 100, }, { writeConcern: { w: "majority", wtimeout: 2000 } });

测试正常提交

模拟一个扣钱动作,其中扣款和流水在一个事务里

session = db.getMongo().startSession({ readPreference: { mode: "primary" } });
balanceCol = session.getDatabase("mongo").balance;
recordCol = session.getDatabase("mongo").record;
session.startTransaction({ readConcern: { level: "local" }, writeConcern: { w: "majority" } }); try {
balanceCol.updateOne({ "name": "Wilson" }, { $set: { "balance": 50 } });
recordCol.insertOne({ "name": "Wilson", change: -50, balance: 50 });
} catch (error) {
session.abortTransaction();
}
session.commitTransaction();
session.endSession();

查看余额情况

db.balance.aggregate([
{ $lookup: { from: "record", localField: "name", foreignField: "name", as: "changs" } },
{ $project: { "_id": 0, "changs._id": 0, "changs.name": 0 } },
]);

结果,可以看到余额扣了,多了一条流水

{ "name" : "Wilson", "balance" : 50, "changs" : [ { "change" : 100, "balance" : 100 }, { "change" : -50, "balance" : 50 } ] }

测试失败回滚

事务内多增加一个插入不存在的集合操作,让事务报错

session.startTransaction({ readConcern: { level: "local" }, writeConcern: { w: "majority" } });
try {
balanceCol.updateOne({ "name": "Wilson" }, { $set: { "balance": -50 } });
recordCol.insertOne({ "name": "Wilson", change: -50, balance: 0 });
session.getDatabase("mongo").user.insert({ "time": new Date() }); //多增加操作一个不存在的表
} catch (error) {
session.abortTransaction();
throw error;
}
session.commitTransaction();
session.endSession();

返回报错信息,显示事务被中断了

2020-04-15T21:37:05.576+0800 E  QUERY    [js] uncaught exception: Error: command failed: {
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1586957825, 1),
"ok" : 0,
"errmsg" : "Transaction 0 has been aborted.",
"code" : 251,
"codeName" : "NoSuchTransaction",
"$clusterTime" : {
"clusterTime" : Timestamp(1586957825, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
doassert@src/mongo/shell/assert.js:18:14
_assertCommandWorked@src/mongo/shell/assert.js:583:17
assert.commandWorked@src/mongo/shell/assert.js:673:16
commitTransaction@src/mongo/shell/session.js:971:17
@(shell):1:1

再查看当前余额情况

db.balance.aggregate([
{ $lookup: { from: "record", localField: "name", foreignField: "name", as: "changs" } },
{ $project: { "_id": 0, "changs._id": 0, "changs.name": 0 } },
]);

可以看到,余额和流水都没变化。

{ "name" : "Wilson", "balance" : 50, "changs" : [ { "change" : 100, "balance" : 100 }, { "change" : -50, "balance" : 50 } ] }

参考文章


Replication — MongoDB Manual

Transactions — MongoDB Manual

转发请标明出处:https://www.cnblogs.com/WilsonPan/p/12708710.html

【Mongodb】事务的更多相关文章

  1. MongoDB - 事务支持

    事务简介 事务是数据库中处理的逻辑单元,每个事务中包括一个或多个数据库操作,既可以是读操作,也可以是写操作. ACID 是一个"真正"事务所需要具备的一组属性集合,指的是原子性(A ...

  2. MongoDB 事务,复制和分片的关系

    摘要:本文尝试对Mongo的复制和分布式事务的原理进行描述,在必要的地方,对实现的正确性进行论证,希望能为MongoDB内核爱好者提供一些参考. 1.前言 MongoDB基于wiredTiger提供的 ...

  3. MongoDB 事务机制

    MongoDB 从4.0 版本开始 副本集支持多文档事务,4.2 版本开始分片集群也支持多文档事务.单个集合的单个文档事务 在 1.x 就支持. 以下是跟 mongo 事务相关的一些概念: 1. Wr ...

  4. SpringBoot整合MongoDB,在多数据源下实现事务回滚。

    项目中用到了MongoDB,准备用来存储业务数据,前提是要实现事务,保证数据一致性,MongoDB从4.0开始支持事务,提供了面向复制集的多文档事务特性.能满足在多个操作,文档,集合,数据库之间的事务 ...

  5. 聊聊MongoDB中连接池、索引、事务

    大家好,我是哪吒. 三分钟你将学会: MongoDB连接池的使用方式与常用参数 查询五步走,能活九十九? MongoDB索引与MySQL索引有何异同? MongoDB事务与ACID 什么是聚合框架? ...

  6. 初窥 MongoDB

    最近在研究Nodejs 自然就接触到了MongoDB  这玩意儿有意思  与关系型数据库相比少了很多条条框框 让我情不自禁的想要了解它的所有 MongoDB与Redis同类 属于NoSql的一种,特点 ...

  7. 使用 Spring Data 进行 MongoDB 4.0 事务处理

    使用 Spring Data 进行 MongoDB 4.0 事务处理 原文链接:http://spring.io/blog/2018/06/28/hands-on-mongodb-4-0-transa ...

  8. 【MongoDB】2019年MongoDB中文社区广州大会,干货满满的分享活动

    1 介绍 MongoDB中文社区(mongoing.com)是大中华区获得官方认可的中文社区,11月23日下午,在广州举办了线下用户大会,带给大家一手干货和实践. 2 大会议程 大会组织者对时间的把控 ...

  9. 分布式缓存 — MongoDB

    --- 数据库管理系统 数据库管理系统主要分为俩大类:RDBMS.NOSQL.在个人电脑.大型计算机和主机上应用最广泛的数据库管理系统是关系型DBMS.NoSQL是对不同于传统的关系数据库的数据库管理 ...

  10. [MongoDB知识体系] 一文全面总结MongoDB知识体系

    MongoDB教程 - Mongo知识体系详解 本系列将给大家构建MongoDB全局知识体系.@pdai MongoDB教程 - Mongo知识体系详解 知识体系 学习要点 学习资料 官网资料 入门系 ...

随机推荐

  1. Java 中的 ==

    简介 如果没有重写 equals 方法, 相当于 == 比较, 即比较两个对象的地址是否相等. 如果是基本数据类型, 直接对值进行比较. code /** * Created by lee on 20 ...

  2. Docker部署RestCloud ETL社区

    镜像说明 nriet/restcloud:latest version:1.3.0 os:slim-buster tomcat:基于官方镜像 tomcat:8.5.78-jdk8-openjdk-sl ...

  3. SciTech-OS-MacOS的CSP(System Integrity Protection)系统正直性保护系统

    bash-3.2# csrutil usage: csrutil <command> Modify the System Integrity Protection configuratio ...

  4. 深度技术解决win11居中任务栏图标变小的方法

    相信很多深度技术的用户都已经安装了win11系统,而win11系统带来的全新界面包括任务栏也进行了改变,有不少的用户使用的时候都感觉任务栏的图标太大了不习惯,为此我们就给大家带来把win11系统居中任 ...

  5. 04Java基础语法之包机制

    包机制 为了更好的组织类,Java提供了包机制,用于区别类名的命名空间. 包语句的语法格式为: package pkg1[. pkg2[. pkg3...]]; 一般会使用公司域名倒置作为包名: 例如 ...

  6. 数据库的<> 比较运算

    数据库的<> 比较运算的等于!= where name <> '张三' 会把NULL的值对应的记录排除掉,所以不包含null和NULL的记录:所以只有''和'李四'才符合条件, ...

  7. java 泛型 反射 动态代理 注解

    https://www.cnblogs.com/20183544-wangzhengshuai/p/16033270.html Java 高级特性有挺多,但是这几个一直没搞太通透,只会简单用用,为什么 ...

  8. 如何在uni-app 平台快速实现一对一音视频通话应用

    "一套代码,多端运行"是很多开发团队的梦想.ZEGO SDK基于uni-app跨平台框架支持iOS.Android.Windows.macOS.HarmonyOS.Web.小程序并 ...

  9. NativeExcel使用

    var Book: IXLSWorkbook; ws: IXLSWorksheet; begin Book := TXLSWorkbook.Create; //建立NativeExcel ws := ...

  10. Solidity学习之代理合约

    什么是代理合约 代理合约针对的是链上合约一经部署无法修改的问题,通过增加一层代理合约,就可以在不修改代理合约代码和地址的前提下,对实际执行的逻辑进行调整,满足了合约升级的需要. 实现逻辑 代理模式将状 ...