我的其他文章也讲解的比较有趣,如果喜欢博主的讲解方式,可以多多支持一下,感谢!
其他优质专栏: 【SpringBoot】【多线程】【Redis】【✨设计模式专栏(已完结)】…等
如果喜欢作者的讲解方式,可以点赞收藏加关注,你的支持就是我的动力
✨更多文章请看个人主页: 码熔burning
看之前可以先了解一下MongoDB是什么:【MongoDB篇】万字带你初识MongoDB!
通过之前的文章,咱们已经学会了对单个文档的 CRUD 操作,也学会了用索引加速查询,用聚合框架分析数据。这些都很重要,但有时候,你的一个业务操作,需要同时修改多个文档,甚至多个集合里的数据!
想象一下,你正在处理一个电商订单:
这三个操作,必须要么全部成功,要么全部失败!如果只成功了第一步和第二步,第三步失败了,那你的订单数据就处于一个“不一致”的状态:订单创建了,库存扣减了,但用户积分没更新,或者更糟,如果是支付场景,钱扣了但订单没创建! 这是绝对不能接受的!
在传统的关系型数据库里,用事务 (Transaction) 来解决这个问题是家常便饭。那在以灵活性著称的 MongoDB 里,事务又是怎么回事呢?是不是像以前听说的那样不支持事务呢?
别急!哥告诉你,MongoDB 已经完全支持多文档 ACID 事务了! 这是 MongoDB 近年来最重要的发展之一!
事务是数据库管理系统执行过程中的一个逻辑单位,它包含一个或多个数据库操作,这些操作被视为一个不可分割的整体。事务的设计目标是确保数据的一致性 (Consistency),通常通过遵循 ACID 原则来实现:
早期的 MongoDB 版本确实不直接支持跨多个文档的 ACID 事务,这在一定程度上限制了它在需要严格数据一致性的复杂业务场景中的应用。
但是!随着技术的进步和社区的需求,从 MongoDB 4.0 版本开始,MongoDB 引入了对复制集 (Replica Sets) 上的多文档 ACID 事务的支持! 随后在 MongoDB 4.2 版本,更是将这个能力扩展到了分片集群 (Sharded Clusters) 上!
这意味着,从 MongoDB 4.0/4.2 及以后的版本开始,你可以在 MongoDB 中安全地执行跨多个文档、多个集合、甚至多个分片的原子性操作了! MongoDB 不再只是“单文档原子性”的数据库了!
注意: 多文档事务需要 MongoDB 部署为复制集 (Replica Set) 或分片集群 (Sharded Cluster)。它不适用于独立的 mongod
实例!
在 MongoDB 中执行事务,需要通过会话 (Session) 来进行。一个会话是一系列逻辑上相关的操作的容器。事务是发生在一个会话中的。
事务的生命周期:
执行一个事务通常遵循以下步骤:
启动一个会话 (Start a Session):获取一个客户端会话对象。
session = db.getMongo().startSession();
或者在使用驱动程序时,通过 MongoClient 对象获取会话。
在会话中启动一个事务 (Start a Transaction):
session.startTransaction();
执行事务操作 (Perform Operations):在事务内部执行你的读写操作。所有需要在同一个事务中执行的数据库操作,都必须使用同一个会话对象来发起!
// 使用会话对象访问数据库和集合,并执行操作
session.getDatabase("mydatabase").collection("accounts").updateOne(
{ account_id: "A001" },
{ $inc: { balance: -100 } }
);
session.getDatabase("mydatabase").collection("accounts").updateOne(
{ account_id: "A002" },
{ $inc: { balance: 100 } }
);
// 你也可以在事务中执行读取操作,通常设置特定的读关注 (Read Concern)
let accountA = session.getDatabase("mydatabase").collection("accounts").findOne({ account_id: "A001" });
注意: 在事务中,你可以执行大部分的 CRUD 操作(insert
, update
, delete
, find
),以及一些索引操作。但有些操作是不允许在事务中执行的,比如创建/删除数据库、创建/删除集合、创建索引(除了某些特定类型的临时索引)、修改用户/角色等管理类操作。
提交事务 (Commit Transaction):如果所有操作都成功,提交事务,所有修改将被原子性地保存到数据库。
session.commitTransaction();
print("Transaction committed.");
中止/回滚事务 (Abort Transaction):如果在事务执行过程中发生任何错误,或者你想取消事务,可以中止事务。中止后,事务中的所有修改都将被回滚,数据库回到事务开始前的状态。
session.abortTransaction();
print("Transaction aborted.");
结束会话 (End Session):事务完成后(无论提交还是中止),结束会话并释放资源。
session.endSession();
典型的事务执行流程 (带错误处理):
在实际应用中,执行事务需要结合错误处理和重试逻辑,因为事务可能会由于各种原因失败(比如网络问题、写冲突、内存不足等)。一个健壮的事务执行通常会放在一个循环里,在特定错误发生时进行重试。
这是一个简化的 Shell 示例:
let session;
try {
session = db.getMongo().startSession();
session.startTransaction({
readConcern: { level: 'snapshot' }, // 在事务中常用 snapshot 读关注
writeConcern: { w: 'majority' } // 在事务中常用 majority 写关注
});
// --- 在这里执行你的事务操作 ---
print("Executing transaction operations...");
// 假设这是一个转账操作
session.getDatabase("mydatabase").collection("accounts").updateOne(
{ account_id: "A001" },
{ $inc: { balance: -100 } }
);
print("Deducted from A001");
session.getDatabase("mydatabase").collection("accounts").updateOne(
{ account_id: "A002" },
{ $inc: { balance: 100 } }
);
print("Credited to A002");
// --- 事务操作结束 ---
session.commitTransaction(); // 提交事务
print("Transaction committed successfully.");
} catch (error) {
// 捕获错误,中止事务
print("Transaction encountered an error: " + error);
if (session && session.inTransaction()) {
session.abortTransaction();
print("Transaction aborted.");
}
// 在这里添加重试逻辑,对于某些可重试的错误进行重试
// ...
} finally {
// 结束会话
if (session) {
session.endSession();
print("Session ended.");
}
}
实际的重试逻辑会更复杂,需要判断错误的类型(是否是可重试的事务错误)。
事务中的读写操作也会受到读关注 (Read Concern) 和写关注 (Write Concern) 的影响,它们决定了事务中读取数据的一致性和写入数据的持久性。
'snapshot'
或 'majority'
读关注。'snapshot'
:提供了一个事务开始时的数据快照,保证了事务内部读到的是一个一致性的视图,不会读到其他并发事务未提交的数据。这是实现事务隔离性 (Isolation) 的关键。'majority'
:保证读到的数据已经被大多数节点确认,持久性更高。'majority'
写关注({ w: 'majority' }
)。这保证了事务提交的数据已经被大多数节点持久化,即使 Primary 节点宕机,数据也不会丢失,提高了持久性 (Durability)。在启动事务时,可以在 startTransaction()
方法的 options 参数中指定读关注和写关注,它们将应用于该事务中的所有操作(除非在单个操作中被覆盖)。
虽然多文档事务非常强大,但它并不是万能的,也有一些限制和需要注意的地方:
mongod
实例不支持。尽管有性能开销和限制,但在以下这些场景中,多文档事务是确保数据一致性不可或缺的工具:
总的来说,如果你需要保证多个独立的写操作“一起成功或一起失败”,那么就应该考虑使用多文档事务。
太棒了!我们详细讲解了 MongoDB 的多文档 ACID 事务!从事务的基本概念和 ACID 原则,到 MongoDB 如何实现单文档原子性和为什么需要多文档事务,再到如何在 MongoDB 中开启、执行、提交、中止事务,以及事务中的读写关注、限制和最佳实践。
核心要点回顾:
'snapshot'
读关注和 'majority'
写关注。掌握了事务,你就掌握了在 MongoDB 中处理复杂业务逻辑和保证数据一致性的利器!这是一个非常重要的技能!
继续加油!让你的数据一致性无懈可击吧!
了解数据库操作请看:【MongoDB篇】MongoDB的数据库操作!
了解集合操作请看:【MongoDB篇】MongoDB的集合操作!
了解文档操作请看:【MongoDB篇】MongoDB的文档操作!
了解索引操作请看:【MongoDB篇】MongoDB的索引操作!
了解聚合操作请看:【MongoDB篇】MongoDB的聚合框架!