MongoDB 从 4.0 开始支持 replica set 级别的多文档事务,4.2 扩展到分片集群事务。虽然 MongoDB 的事务不如关系型数据库成熟,但在需要强一致性的场景下非常有用。
1. ACID 保证
| ACID | MongoDB 支持 | 说明 |
|---|---|---|
| Atomicity | ✅ | 事务内所有操作同时成功或同时回滚 |
| Consistency | ✅ | 遵守文档验证规则和索引约束 |
| Isolation | ✅ Snapshot | 快照隔离(Repeatable Read 级别) |
| Durability | ✅(可配置) | journal 持久化 |
2. 事务基础用法
2.1 Mongo Shell
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;
// 扣减库存
inventory.updateOne(
{ sku: "SKU001" },
{ $inc: { qty: -2 } },
{ session }
);
// 创建订单
orders.insertOne({
userId: "u123",
items: [{ sku: "SKU001", qty: 2 }],
status: "paid",
createdAt: new Date()
}, { session });
session.commitTransaction();
} catch (error) {
session.abortTransaction();
throw error;
} finally {
session.endSession();
}
2.2 驱动程序(Java)
MongoClient client = MongoClients.create("mongodb://localhost:27017");
ClientSession session = client.startSession();
TransactionOptions txnOptions = TransactionOptions.builder()
.readConcern(ReadConcern.SNAPSHOT)
.writeConcern(WriteConcern.MAJORITY)
.readPreference(ReadPreference.primary())
.build();
TransactionBody<String> txnBody = () -> {
MongoDatabase db = client.getDatabase("shop");
db.getCollection("inventory").updateOne(
session, Filters.eq("sku", "SKU001"),
Updates.inc("qty", -2)
);
db.getCollection("orders").insertOne(session,
new Document("userId", "u123")
.append("status", "paid")
.append("createdAt", new Date())
);
return "Order created successfully";
};
try {
String result = session.withTransaction(txnBody, txnOptions);
System.out.println(result);
} catch (RuntimeException e) {
// 事务自动回滚
throw e;
} finally {
session.close();
}
3. 事务限制
| 限制 | 说明 |
|---|---|
| 事务超时 | 默认 60 秒(maxTransactionLockRequestTimeoutMillis) |
| 文档修改 | 事务内无法创建/删除集合和索引 |
| 操作数量 | 无硬性限制,但受 oplog 大小限制 |
| 分片事务 | 涉及多个分片时性能下降明显 |
| 会话 | 每个事务必须绑定一个会话 |
4. 性能影响
单文档写入: 50,000+ ops/s
多文档事务: 5,000-10,000 ops/s (事务开销约 5-10x)
事务延迟来源:
- 获取快照(Snapshot)
- 两阶段提交协调(分片事务)
- oplog 同步
最佳实践:能用单文档原子操作(update + $inc 等)就不用事务。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。