mongodb 4.0+多文档事务的实现原理
1. 副本集事务实现(4.0+)
- 非严格依赖二阶段提交
MongoDB 4.0 在副本集环境中通过 全局逻辑时钟(Logical Clock) 和 快照隔离(Snapshot Isolation) 实现多文档事务,事务提交时通过原子性协议(如 Raft 共识算法)协调副本集成员,而非传统 2PC 模式。
事务提交时,主节点将事务操作日志(oplog 条目)广播至副本集成员。成员需通过 多数派确认协议(类似 Raft 的日志复制机制)达成共识,确保日志持久化后才标记事务为已提交。 - ACID 保障
事务操作在提交时通过 WiredTiger 存储引擎的 MVCC(多版本并发控制)和日志持久化机制,确保原子性和持久性,且读操作基于一致性快照。
2. 分片集群事务实现(4.2+)
- 依赖二阶段提交
4.2 版本引入的分布式事务(跨分片操作)需通过 两阶段提交协议 协调多个分片:- 准备阶段:各分片节点预提交事务并记录状态;
- 提交阶段:事务协调器确认所有分片就绪后,全局提交事务。
- 扩展性优化
通过减少跨节点锁竞争和优化状态管理,降低 2PC 对性能的影响,但跨分片事务仍存在更高的延迟
然而,MySQL 的 MVCC 机制 会保留旧版本数据,但其实现方式与 MongoDB 存在显著差异。以下从底层设计、数据存储与清理角度解释其工作原理:
3. MVCC 的核心机制:undo log 与版本链
- 旧版本数据存储方式
MySQL InnoDB 引擎通过 undo log(回滚日志) 保留旧版本数据。每次更新操作时,原始数据会被复制到 undo log 中形成版本链,新数据直接写入主数据页13。
示例:事务 A 修改某行数据时,该行原始值会被写入 undo log,新值更新到主数据页,形成两个版本。 - 版本可见性规则
事务通过 Read View(一致性视图)判断可见性。每个事务启动时生成一个 Read View,记录当前活跃事务 ID 列表,仅能读取 已提交且版本时间戳 ≤ 事务快照时间戳 的数据。 - 2. 旧版本数据的生命周期
- 临时性保留
旧版本数据仅在 活跃事务需要访问 时保留。例如,若事务 B 在事务 A 提交前启动,事务 B 需通过 undo log 读取事务 A 修改前的旧版本数据。 - 自动清理机制
InnoDB 后台的 purge 线程 会定期清理 无活跃事务依赖的旧版本数据(如 undo log 中已提交且无其他事务引用的旧记录),避免长期堆积导致存储膨胀。
4. 用户感知的“未保留旧数据”现象
- 快速清理与隐式存储
由于 undo log 的设计,旧版本数据对用户透明(不直接体现在数据文件中),且清理效率高。用户通常感知不到旧版本的存在,误认为 MySQL 未保留旧数据。 - 与 MongoDB 的差异
MongoDB 的 WiredTiger 引擎通过 显式版本链 存储旧数据(如 B+ 树多版本节点),而 MySQL 依赖 undo log 的日志结构 实现版本管理,两者底层存储方式不同。
6. MongoDB 事务有没有像MySQL一样,实现WAL(Journal 日志) 和oplog 的二阶段提交?
因为Journal 和 oplog 没有能联系起来的标识位(xid).
6.1. WAL(Write-Ahead Logging)的作用
- 存储引擎层持久化
MongoDB 的 WiredTiger 存储引擎通过 WAL(Journal 日志) 实现数据持久化。所有事务操作会先写入 WAL 日志,确保在崩溃恢复时能通过重放日志恢复未提交的事务或已提交但未落盘的数据。 - 检查点机制
WiredTiger 定期(默认每分钟)创建检查点(Checkpoint),将内存中的脏页(Dirty Page)批量写入磁盘,并清理已持久化的 WAL 日志,减少恢复时间。
6.2. oplog 的副本集同步机制
- 操作日志(oplog)的核心功能
oplog 是 MongoDB 副本集的核心组件,记录所有数据变更操作(如插入、更新、删除)。事务提交时,主节点将事务内的操作打包为 原子性 oplog 条目 广播至副本集成员,成员通过重放 oplog 实现数据同步。 - 原子性提交协议
事务提交依赖 多数派确认机制(类似 Raft 的日志复制流程),而非传统 2PC。主节点需等待多数副本集成员确认 oplog 持久化后,才标记事务为已提交,确保数据一致性。