MongoDB4.0事务采坑
发布于 9 天前 作者 MedusaLeee 604 次浏览 最后一次编辑是 8 天前 来自 分享

详细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

3 回复

事物 -> 事务

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

来自酷炫的 CNodeMD

可以的 4.0终于来了

回到顶部