MongoDB入门学习教程,从入门到精通,MongoDB事务知识点梳理(8)

MongoDB事务知识点梳理

1. 事务简介

1.1 事务的背景与必要性

在MongoDB中,对单个文档的操作天生就是原子的。得益于MongoDB强大的文档模型,开发者可以将相关数据嵌入到单个文档中,从而在许多场景下避免了对跨文档事务的需求。

然而,在金融、会计等对数据一致性要求极高的场景中,应用程序可能需要对多个文档(可能跨多个集合或数据库)执行原子性读写操作。为此,MongoDB从4.0版本开始引入了多文档事务 ,并在4.2版本升级为分布式事务,使其能够运行在分片集群上。

1.2 ACID特性

MongoDB事务完全支持ACID特性,确保数据的可靠性和一致性:

  • 原子性(Atomicity):事务中的所有操作要么全部成功提交,要么全部失败回滚。不存在部分成功的情况。
  • 一致性(Consistency):事务开始前和结束后,数据库都处于一致的状态。如果事务中止,所有更改将被丢弃,数据回滚到事务开始前的状态。
  • 隔离性(Isolation) :MongoDB默认使用快照隔离(Snapshot Isolation)。在事务提交之前,事务中所做的数据更改对事务外部是不可见的,从而避免了脏读、不可重复读等现象。
  • 持久性(Durability):一旦事务提交成功,其修改的数据将被永久保存。MongoDB通过预写日志(Journal)机制确保即使在系统故障或宕机后,已提交的事务也不会丢失。

1.3 隔离级别详解

MongoDB的WiredTiger存储引擎支持多种隔离级别,默认采用快照隔离

  • 脏读:指一个事务读取了另一个未提交事务的数据。在快照隔离下,MongoDB事务只能读取已提交的数据,因此避免了脏读。
  • 不可重复读:指在一个事务内,两次读取同一数据得到不同结果。由于事务基于快照读取,快照在整个事务生命周期内是一致的,因此避免了不可重复读。
  • 幻读:指一个事务读取某个范围的数据时,另一个事务插入了新数据,导致再次读取时数据条数发生变化。快照隔离也能有效防止幻读。

1.4 事务与原子性的关系

特性 单文档操作 多文档事务
原子性范围 单个文档的修改 跨多个文档、集合、数据库的修改
适用场景 大多数常规读写场景 强一致性要求的金融、库存扣减等场景
性能开销 相对较高(涉及锁、两阶段提交等)

2. 如何使用事务

2.1 前置条件与配置

在使用事务前,MongoDB环境需满足以下要求:

  • 版本要求:副本集事务需MongoDB 4.0+,分片集群分布式事务需MongoDB 4.2+。
  • 部署模式 :事务只能在副本集分片集群上使用,单机模式不支持多文档事务。
  • 存储引擎:必须使用WiredTiger存储引擎。

2.2 事务API核心组件

MongoDB事务基于会话(Session) 实现。所有事务操作都必须在同一个会话中完成。

  • 启动会话 :通过 startSession() 创建客户端会话。
  • 开启事务 :在会话中调用 startTransaction()
  • 执行操作:执行具体的CRUD操作(注意:必须将session作为参数传递给每个操作)。
  • 提交/回滚 :所有操作成功则调用 commitTransaction() 提交;捕获异常则调用 abortTransaction() 回滚。

2.3 完整事务示例

mongo Shell 示例

javascript 复制代码
// 1. 开启会话
session = db.getMongo().startSession();

// 2. 获取集合(通过会话获取)
coll1 = session.getDatabase("mydb1").foo;
coll2 = session.getDatabase("mydb2").bar;

// 3. 开启事务,并设置读/写关注
session.startTransaction({
    readConcern: { level: "snapshot" },
    writeConcern: { w: "majority" }
});

try {
    // 4. 执行事务内的操作
    coll1.insertOne({ abc: 1 });
    coll2.insertOne({ xyz: 999 });
    
    // 5. 提交事务
    session.commitTransaction();
    print("事务提交成功");
} catch (error) {
    // 6. 出错则中止事务
    session.abortTransaction();
    print("事务回滚:" + error);
} finally {
    session.endSession();
}

回调API(推荐)

现代驱动通常提供 withTransaction 回调方法,自动处理提交和重试逻辑:

javascript 复制代码
const session = client.startSession();
const callback = async (session) => {
    const coll1 = client.db("mydb1").collection("foo");
    const coll2 = client.db("mydb2").collection("bar");
    
    await coll1.insertOne({ abc: 1 }, { session });
    await coll2.insertOne({ xyz: 999 }, { session });
};

try {
    await session.withTransaction(callback);
    // 事务成功提交
} catch (error) {
    // 事务最终失败
} finally {
    await session.endSession();
}

2.4 事务中的支持操作

MongoDB事务支持大部分CRUD操作,但存在一些限制:

支持的操作 不支持的操作
insert / update / delete 写入 system.*configadminlocal 集合
find 查询 写入 Capped Collection(固定大小集合)
aggregate(聚合) explain 命令
findAndModify listCollections / listIndexes 命令
在事务内创建集合/索引(MongoDB 4.4+) 在跨分片写事务中创建新集合

3. 对应用程序的事务限制进行调优

3.1 运行时限制与超时

  • 事务超时 :默认情况下,MongoDB会自动中止运行时间超过 60秒 的事务(参数:transactionLifetimeLimitSeconds)。
  • 锁获取超时 :事务在等待获取锁时,如果等待超过 5毫秒 (参数:maxTransactionLockRequestTimeoutMillis),事务将被终止。
  • 优化建议:将大型事务拆分为多个小型事务,并确保查询使用了合适的索引,以缩短执行时间。

3.2 大小限制与文档数量

限制类型 MongoDB 4.0 MongoDB 4.2+ 推荐实践
事务日志大小 单个oplog条目限制 16MB 支持多个oplog条目,无硬性限制 控制事务大小在 16MB 以内
修改文档数 无硬性限制 无硬性限制 建议每个事务修改文档数不超过 1000

3.3 并发控制与写冲突(Write Conflicts)

当多个事务同时尝试修改同一个文档时,会发生写冲突。MongoDB通过MVCC机制处理并发:

  • 乐观锁机制 :事务开始时获取数据的快照版本,提交时检查版本是否变更。如果版本已变,提交会失败并抛出 WriteConflict 错误。
  • 重试机制 :对于临时性冲突,驱动会自动重试事务(使用 withTransaction API 时)。
  • 最佳实践:尽量避免在事务内外同时修改同一文档;减少事务的持有时间,以降低冲突概率。

3.4 读/写关注点优化

合理设置读关注(Read Concern)和写关注(Write Concern)可以平衡性能与一致性:

关注点类型 常用级别 说明
读关注 "local" 默认值,性能最好,可能读到被回滚的数据
"majority" 只读已提交的持久化数据,保证一致性
"snapshot" 事务级别使用,确保从快照中读取,避免幻读
写关注 { w: 1 } 主节点确认即可,性能较高
{ w: "majority" } 大多数节点确认,保证持久性,事务建议使用此级别

3.5 常见问题与规避措施

  1. 长事务导致缓存压力:长事务会阻止WiredTiger缓存中的旧版本数据被释放,可能导致缓存压力过大甚至"死锁"。应尽早提交或回滚事务。
  2. DDL阻塞事务:在事务中尽量避免执行DDL操作(如创建索引),此类操作会获取全局锁,阻塞其他事务。
  3. 跨分片事务开销:跨分片的事务(分布式事务)性能开销较大。在数据建模时,尽量将频繁参与事务的文档放在同一个分片上。

3.6 性能调优清单

  • 数据建模:优先使用嵌入式文档,减少对事务的依赖。
  • 索引优化:确保事务中的查询拥有合适的索引,避免全表扫描。
  • 事务大小:控制修改文档数量在1000以内,oplog大小在16MB以内。
  • 超时设置 :根据业务场景调整 transactionLifetimeLimitSeconds 参数。
  • 客户端重试 :编写完善的异常捕获和重试逻辑(尤其是 TransientTransactionError)。
  • 版本选择:如果重度使用事务,建议升级至 MongoDB 5.0+ 以获取更多稳定性优化和内核缺陷修复。
相关推荐
LaughingZhu2 小时前
Product Hunt 每日热榜 | 2026-03-29
数据库·人工智能·经验分享·神经网络·chatgpt
jialan752 小时前
不干胶管理
大数据·数据库
red_redemption2 小时前
自由学习记录(149)
学习
EasyCVR2 小时前
插件模块化集成设计:花屏蓝屏画面模糊检测...EasyCVR视频质量诊断功能的技术与落地逻辑
服务器·数据库·音视频·视频质量诊断
|华|2 小时前
mysql的备份与恢复
数据库·mysql
java资料站2 小时前
milvus向量数据库
数据库·milvus
chushiyunen2 小时前
langgraph笔记
数据库·人工智能·笔记
切糕师学AI2 小时前
PostgreSQL 中的 pg_trgm GIN 索引详解
数据库·postgresql·gin·索引·pg_grgm
爱丽_2 小时前
MySQL 锁与死锁:行锁、间隙锁、Next-Key Lock 与排查手册
数据库·mysql