MongoDB4.0事务采坑
发布于 1 个月前 作者 MedusaLeee 1563 次浏览 来自 分享

详细demo地址

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();

结果

mongo-driver-result.png

由结果看多表单表事务都是支持的,且事务未结束前,出于事务中的操作是不会真实入库的。

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();

结果

此例子没有测试多表,结果和上面的测试一样,只是不同的驱动。

mongoose-result.png

11 回复

事物 -> 事务

@alsotang 您说的对,只怪语文是体育老师教的,哈哈,一会就改

来自酷炫的 CNodeMD

可以的 4.0终于来了

现在官方给出的文档,没有给出mongodb4.0 nodejs drive的文档啊 image.png

那么问题来了,在哪看文档呢?

而且 npm search搜出来的 nodejs drive 大于3.1 版本的只有一个 3.1.1 ,也是是说只有这一个4.0的版本可用呢? image.png

用到事务还不如上pgsql了。。。

@jjeejj Nodejs mongodb 驱动的版本 > 3.1.0就可以

来自酷炫的 CNodeMD

@waitingsong 您说的是,有总比没有好

来自酷炫的 CNodeMD

楼主研究过mongodb的事务隔离级别吗? snapshot感觉好复杂。

来自酷炫的 CNodeMD

@waitingsong mongodb的好处业务建模快 结构灵活 所想即所得 加了事务如虎添翼

@phper-chen 比数据结构,mongo和pg不是一个级别的吧。

回到顶部