在 MongoDB 4.0 及更高版本中,支持分布式事务,这意味着你可以在多个文档和集合中执行 ACID(原子性、一致性、隔离性、持久性)事务,这些文档和集合甚至可以跨多个分片集合。分布式事务在需要跨多个集合或数据库进行原子操作时非常有用。
一、分布式事务简介
分布式事务允许你将一组读写操作封装在一个事务中,并确保这些操作要么全部成功,要么全部失败。MongoDB 使用二阶段提交协议(2PC)来管理分布式事务。
二、前提条件
- MongoDB 版本:分布式事务功能在 MongoDB 4.0 及更高版本中可用。
- 分片集群:分布式事务支持跨分片集群的操作,但你也可以在非分片环境中使用事务。
三、使用分布式事务
1. 使用 MongoDB Shell 执行事务
以下是一个使用 MongoDB Shell 在跨集合中执行分布式事务的示例代码。
假设我们有两个集合:accounts 和 transactions。我们要将 accounts 中一个账户的余额减少 $100,并在 transactions 中记录这次交易。
javascript
// 连接到 MongoDB
conn = new Mongo();
db = conn.getDB("mydatabase");
// 开始事务会话
session = db.getMongo().startSession();
session.startTransaction();
try {
// 获取会话数据库
accountsCollection = session.getDatabase("mydatabase").getCollection("accounts");
transactionsCollection = session.getDatabase("mydatabase").getCollection("transactions");
// 执行账户余额更新操作
accountsCollection.updateOne(
{ _id: 1 },
{ $inc: { balance: -100 } }
);
// 插入交易记录
transactionsCollection.insertOne({
accountId: 1,
amount: -100,
timestamp: new Date()
});
// 提交事务
session.commitTransaction();
print("Transaction committed.");
} catch (error) {
// 回滚事务
print("Transaction aborted due to an error: " + error);
session.abortTransaction();
} finally {
// 结束会话
session.endSession();
}
2. 使用 Node.js 和 Mongoose 执行事务
以下是一个使用 Node.js 和 Mongoose 在跨集合中执行分布式事务的示例代码。
首先,确保你已经安装了 mongoose:
bash
npm install mongoose
然后,编写以下代码:
javascript
const mongoose = require('mongoose');
const uri = 'mongodb://localhost:27017/mydatabase';
mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
const accountSchema = new mongoose.Schema({
_id: Number,
balance: Number
});
const transactionSchema = new mongoose.Schema({
accountId: Number,
amount: Number,
timestamp: Date
});
const Account = mongoose.model('Account', accountSchema);
const Transaction = mongoose.model('Transaction', transactionSchema);
async function performTransaction() {
const session = await mongoose.startSession();
session.startTransaction();
try {
// 执行账户余额更新操作
await Account.updateOne(
{ _id: 1 },
{ $inc: { balance: -100 } },
{ session: session }
);
// 插入交易记录
await Transaction.create([{
accountId: 1,
amount: -100,
timestamp: new Date()
}], { session: session });
// 提交事务
await session.commitTransaction();
console.log('Transaction committed.');
} catch (error) {
// 回滚事务
console.log('Transaction aborted due to an error:', error);
await session.abortTransaction();
} finally {
// 结束会话
session.endSession();
}
}
performTransaction()
.then(() => mongoose.disconnect())
.catch(err => console.error('Error:', err));
四、重要注意事项
- 事务大小限制:MongoDB 对事务的大小和操作数量有一定限制。每个事务的 oplog 不能超过 16MB,且事务中的写操作数不能超过 1000。
- 写冲突:在高并发环境中,可能会发生写冲突,导致事务失败。你需要在应用程序中处理这些情况,可能需要重试事务。
- 性能影响:事务在性能上会有一定的开销,因为需要确保 ACID 属性。因此,只在确实需要事务的情况下使用它们。
- 事务超时 :MongoDB 会自动中止运行时间超过
transactionLifetimeLimitSeconds配置的事务。默认值通常是 60 秒。 - 事务隔离性:MongoDB 的事务提供快照隔离级别,这意味着在事务开始时看到的数据快照在整个事务期间保持一致。
五、总结
分布式事务在处理需要跨集合或数据库的原子操作时非常有用。通过使用二阶段提交协议,MongoDB 确保了事务操作的原子性和一致性。无论是在 MongoDB Shell 中,还是在应用程序代码中(例如使用 Node.js 和 Mongoose),你都可以灵活地使用事务来确保数据的完整性。请务必注意其使用的限制和性能影响,确保在需要的情况下合理使用事务。