MongoDB 事务功能使用方法详解

前言

MongoDB 是一种基于文档的 NoSQL 数据库,它具有良好的扩展性和可靠性。在开发中,为了保护数据完整性和一致性,我们经常需要使用数据库的事务功能。

MongoDB 从版本 4.0 开始支持多文档事务,通过事务功能可以确保多个操作的原子性,即所有操作要么全部成功,要么全部失败。在本文中,我们将详细介绍 MongoDB 事务功能的使用方法,并提供代码示例。

MongoDB 事务的基本概念

在 MongoDB 中,一个事务由多个操作组成,每个操作被称为一个“事务操作”,它们可以是 CRUD 操作、索引操作等。MongoDB 中的事务具有以下特点:

  • 支持多文档事务
  • 操作必须是 ACID 的(原子性、一致性、隔离性、持久性)
  • 所有操作必须在同一个事务中进行
  • 事务操作执行顺序是按照指定顺序执行的

MongoDB 事务的使用方法

为了使用 MongoDB 的事务功能,我们需要先创建一个事务会话,并在会话中执行事务操作。下面我们将分别介绍如何创建事务会话和执行事务操作。

创建事务会话

const session = db.getMongo().startSession({ mode: 'primary' });

一个事务会话是由 MongoDB 返回的一个实例。我们首先需要使用 getMongo() 方法获取 MongoDB 的实例,然后调用 startSession() 方法创建一个事务会话,可以通过 mode 参数指定会话模式,包括 primarysecondaryprimaryPreferred

执行事务操作

在事务会话中,我们可以通过 withTransaction() 方法执行事务操作,例如:

session.withTransaction(async () => {
  await col1.insertOne({ name: 'foo' });
  await col2.updateOne({ title: 'bar' }, { $set: { content: 'foo' } });
  await col3.deleteMany({ age: { $gt: 30 } });
}, {
  readConcern: { level: 'snapshot' },
  writeConcern: { w: 'majority' },
  maxTimeMS: 120000
})
  • withTransaction() 方法接收两个参数,第一个参数是一个异步函数,表示需要执行的事务操作。我们可以在这个函数中调用任意数量的数据库操作,它们将按照指定顺序执行。
  • 第二个参数是一个配置对象,它可以用来设置事务的读写关注(readConcern、writeConcern)和最大执行时间(maxTimeMS)。

需要注意的是,在事务中我们必须使用相同的会话对象(session),否则事务会失败。

回滚事务

如果事务中的任何一个操作失败,MongoDB 会自动回滚整个事务,我们也可以手动回滚事务。例如:

try {
  session.withTransaction(async () => {
    await col1.insertOne({ name: 'foo' });
    await col2.updateOne({ title: 'bar' }, { $set: { content: 'foo' } });
    await col3.deleteMany({ age: { $gt: 30 } });
  }, {
    readConcern: { level: 'snapshot' },
    writeConcern: { w: 'majority' },
    maxTimeMS: 120000
  })
} catch (err) {
  await session.abortTransaction();
  console.log('Transaction aborted.');
}

withTransaction() 方法内部,我们可以使用 throw new Error('Error!'); 来抛出异常,这会导致事务失败并自动回滚。在异常处理中,我们可以调用 session.abortTransaction() 方法手动回滚事务。

MongoDB 事务的示例代码

下面是使用 MongoDB 事务的一个示例,我们将在三个集合中执行类似于转账的操作,并确保这些操作具有原子性,要么全部成功,要么全部失败。

const session = db.getMongo().startSession({ mode: 'primary' });
const col1 = db.collection('accounts1');
const col2 = db.collection('accounts2');
const col3 = db.collection('logs');

session.withTransaction(async () => {
  // 更新账号1的金额,扣除100元
  const res1 = await col1.updateOne({ accountNumber: '123456' }, { $inc: { balance: -100 }});
  if (res1.matchedCount !== 1 || res1.modifiedCount !== 1) {
    throw new Error('Error!');
  }

  // 更新账号2的金额,增加100元
  const res2 = await col2.updateOne({ accountNumber: '789012' }, { $inc: { balance: 100 }});
  if (res2.matchedCount !== 1 || res2.modifiedCount !== 1) {
    throw new Error('Error!');
  }

  // 记录日志
  await col3.insertOne({ from: '123456', to: '789012', amount: 100, timestamp: new Date() });
}, {
  readConcern: { level: 'snapshot' },
  writeConcern: { w: 'majority' },
  maxTimeMS: 120000
});

await session.endSession();

在上面的示例中,我们首先创建了一个事务会话,然后使用 withTransaction() 方法执行三个操作:扣除 accountNumber123456 的账号 100 元、增加 accountNumber789012 的账号 100 元、记录日志。如果任何一个操作失败,事务会回滚。

最后,我们使用 endSession() 方法结束会话。

总结

本文介绍了 MongoDB 的事务功能的基本概念和使用方法,包括创建事务会话、执行事务操作、回滚事务等。MongoDB 的事务功能可以保证多个操作的原子性和一致性,是开发中重要的工具。希望本文能够对广大前端开发者有所帮助。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a22ee5add4f0e0ffa3ed0b


纠错反馈