MongoDB4.0事务采坑
如有错误,还请指正。
版本
MongoDB server version: 4.0.0
限制
MongoDB有三种部署方式,分别是单实例、副本集和分布式部署。目前只有副本集支持事务, 所以只能现部署副本集的MongoDB集群了。
部署MongoDB副本集集群
在我的Mac已经安装3.x版本,为了不影响开发,我们使用docker
部署。
部署示例
MongoDB Nodejs Driver Demo
Nodejs mongodb库的版本 > 3.1.0
代码
const {MongoClient} = require('mongodb');
const uri = 'mongodb://192.168.31.93:27017,192.168.31.93:27018/mongo-tx';
const oneTableTest = async () => {
const client = await MongoClient.connect(uri, {useNewUrlParser: true, replicaSet: 'rs2'});
const db = client.db();
// 删除表内老数据
await db.collection('order').deleteMany({});
// 成功完成一个事务
// 开启事务
let session = client.startSession();
await session.startTransaction();
// 插入一条新数据
await db.collection('order').insert({
name: 'order1',
total: 50
}, {session});
await session.commitTransaction();
session.endSession();
let count = await db.collection('order').countDocuments();
console.log(`oneTableTest-现在order表中有数据${count}条`);
// 事务回滚
// 开启事务
session = client.startSession();
await session.startTransaction();
try {
// 插入一个正确的订单
await db.collection('order').insert({
name: 'order2',
total: 100
}, {session}); // 每次操作都得带上 session
count = await db.collection('order').countDocuments();
console.log(`oneTableTest-现在order表中有数据${count}条`);
// 抛出一个异常
throw new Error('订单异常');
} catch (e) {
// 有异常,终止事务
console.log('异常,回滚事务');
// 执行完成后,发现name为order2的订单 没有插入数据库
await session.abortTransaction();
session.endSession();
count = await db.collection('order').countDocuments();
console.log(`oneTableTest-现在order表中有数据${count}条`);
}
};
const multiTableTest = async () => {
const client = await MongoClient.connect(uri, {useNewUrlParser: true, replicaSet: 'rs2'});
const db = client.db();
// 删除表内老数据
await db.collection('order').deleteMany({});
await db.collection('product').deleteMany({});
// 插入一条新数据
await db.collection('order').insert({
name: 'order1',
total: 50
});
await db.collection('product').insert({
name: 'product1',
price: 50
});
let orderCount = await db.collection('order').countDocuments();
console.log(`multiTableTest-现在order表中有数据${orderCount}条`);
let productCount = await db.collection('product').countDocuments();
console.log(`multiTableTest-现在product表中有数据${productCount}条`);
// 开启事务
const session = client.startSession();
await session.startTransaction();
try {
// 插入一个正确的订单
await db.collection('order').insert({
name: 'order2',
total: 100
}, {session}); // 每次操作都得带上 session
// 插入一个正确的商品
await db.collection('product').insert({
name: 'product2',
price: 100
}, {session}); // 每次操作都得带上 session
orderCount = await db.collection('order').countDocuments();
console.log(`multiTableTest-现在order表中有数据${orderCount}条`);
productCount = await db.collection('product').countDocuments();
console.log(`multiTableTest-现在product表中有数据${productCount}条`);
// 抛出一个异常
throw new Error('多表异常');
} catch (e) {
// 有异常,终止事务
console.log('多表异常,回滚事务');
// 执行完成后,发现name为order2的订单,name为product2的商品都没有插入数据库
await session.abortTransaction();
session.endSession();
orderCount = await db.collection('order').countDocuments();
console.log(`multiTableTest-现在order表中有数据${orderCount}条`);
productCount = await db.collection('product').countDocuments();
console.log(`multiTableTest-现在product表中有数据${productCount}条`);
}
};
const main = async () => {
await oneTableTest();
await multiTableTest();
};
main().then();
结果
由结果看多表
和单表
事务都是支持的,且事务未结束前,出于事务中的操作是不会真实入库的。
Mongoose Demo
mongoose库的版本 > 5.2.0
代码
const mongoose = require('mongoose');
const uri = 'mongodb://192.168.31.93:27017,192.168.31.93:27018/mongo-tx';
const oneTableTest = async () => {
await mongoose.connect(uri, { useNewUrlParser: true, replicaSet: 'rs2' });
const Order = mongoose.model('Order', new mongoose.Schema({
name: String,
total: Number
}));
await Order.remove({});
let count = await Order.countDocuments({});
console.log(`oneTableTest-现在order表中有数据${count}条`);
// 正常事务
let session = await mongoose.startSession();
await session.startTransaction();
await Order.create({
name: 'order3',
total: 150
});
await session.commitTransaction();
session.endSession();
count = await Order.countDocuments({});
console.log(`oneTableTest-现在order表中有数据${count}条`);
// 事务回滚
session = await mongoose.startSession();
await session.startTransaction();
try {
// 这种写法不行 create方法不接收 options参数,如果要接收options参数,第一参数必须为Array
// 见文档 http://mongoosejs.com/docs/api.html#create_create
// await Order.create({
// name: 'order4',
// total: 200
// }, {session});
// 这种写法可以
// await Order({
// name: 'order4',
// total: 200
// }).save({session});
// 这种写法也可以
await Order.create([{
name: 'order4',
total: 200
}], {session});
count = await Order.countDocuments({});
console.log(`oneTableTest-现在order表中有数据${count}条`);
// 抛出一个异常
throw new Error('订单异常');
} catch (e) {
// 有异常,终止事务
console.log('异常,回滚事务');
await session.abortTransaction();
session.endSession();
count = await Order.countDocuments();
console.log(`oneTableTest-现在order表中有数据${count}条`);
}
};
oneTableTest().then();
结果
此例子没有测试多表,结果和上面的测试一样,只是不同的驱动。
事物 -> 事务
可以的 4.0终于来了
现在官方给出的文档,没有给出mongodb4.0 nodejs drive
的文档啊
那么问题来了,在哪看文档呢?
而且 npm search
搜出来的 nodejs drive
大于3.1 版本的只有一个 3.1.1 ,也是是说只有这一个4.0的版本可用呢?
用到事务还不如上pgsql了。。。
@waitingsong 您说的是,有总比没有好
来自酷炫的 CNodeMD
楼主研究过mongodb的事务隔离级别吗? snapshot感觉好复杂。
来自酷炫的 CNodeMD
@jjeejj 事务在3.1 nodejs driver api里面有,https://mongodb.github.io/node-mongodb-native/3.1/api/ClientSession.html
来自酷炫的 CNodeMD
@waitingsong mongodb的好处业务建模快 结构灵活 所想即所得 加了事务如虎添翼
@phper-chen 比数据结构,mongo和pg不是一个级别的吧。