MongoDB 事务
事务基础(mongosh)
多文档事务需要副本集或分片集群,自 MongoDB 4.0 起可用。
// Start a session and transaction
const session = db.getMongo().startSession();
session.startTransaction({
readConcern: { level: "snapshot" },
writeConcern: { w: "majority" }
});
try {
const orders = session.getDatabase("shop").orders;
const inventory = session.getDatabase("shop").inventory;
orders.insertOne(
{ userId: "u1", productId: "p1", qty: 2, total: 49.98 },
{ session }
);
inventory.updateOne(
{ productId: "p1" },
{ $inc: { stock: -2 } },
{ session }
);
session.commitTransaction();
print("Transaction committed");
} catch (err) {
session.abortTransaction();
print("Transaction aborted:", err);
} finally {
session.endSession();
}
Node.js 驱动示例
// Using withTransaction() helper (auto-retry on transient errors)
const { MongoClient } = require("mongodb");
const client = new MongoClient(uri);
async function transferFunds(fromId, toId, amount) {
const session = client.startSession();
try {
await session.withTransaction(async () => {
const accounts = client.db("bank").collection("accounts");
const from = await accounts.findOne({ _id: fromId }, { session });
if (from.balance < amount) throw new Error("Insufficient funds");
await accounts.updateOne(
{ _id: fromId },
{ $inc: { balance: -amount } },
{ session }
);
await accounts.updateOne(
{ _id: toId },
{ $inc: { balance: amount } },
{ session }
);
}, {
readConcern: { level: "snapshot" },
writeConcern: { w: "majority" },
readPreference: "primary"
});
} finally {
await session.endSession();
}
}
可重试写入
可重试写入在网络错误和主节点故障转移时自动重试一次,驱动 3.6+ 版本默认启用。
// Enable in connection string (default in modern drivers)
const client = new MongoClient("mongodb://host:27017/db?retryWrites=true");
// Retryable writes work for:
// - insertOne, insertMany
// - updateOne, replaceOne
// - deleteOne
// - findOneAndUpdate, findOneAndReplace, findOneAndDelete
// - bulkWrite (ordered and unordered)
// NOT retryable: multi-document writes (updateMany, deleteMany)
// NOT retryable: operations inside transactions (managed separately)
// Disable retryable writes (e.g., for legacy compatibility)
const client2 = new MongoClient(uri, { retryWrites: false });
因果一致性
因果一致性确保会话读取自己的写入,即使针对从节点也是如此。
// Causally consistent session
const session = client.startSession({ causalConsistency: true });
// Write to primary
await db.collection("settings").updateOne(
{ key: "theme" },
{ $set: { value: "dark" } },
{ session, writeConcern: { w: "majority" } }
);
// Read from secondary — guaranteed to see the write above
await db.collection("settings").findOne(
{ key: "theme" },
{
session,
readPreference: "secondary",
readConcern: { level: "majority" }
}
);
事务限制与最佳实践
| 限制 | 默认值/建议 |
|---|---|
| 最大事务时长 | 60 seconds (transactionLifetimeLimitSeconds) |
| 最大文档锁数量 | 无硬性限制,但应保持少量 |
| Oplog 条目大小限制 | 16 MB per transaction |
| 事务中建集合 | MongoDB 4.4 起支持 |
| 保持事务简短 | 减少锁争用和 WiredTiger 冲突 |
| 避免长时间运行的查询 | 事务内优先使用预计算或索引查找 |