文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

MongoDB 写操作详细流程分析【何时产生 WT 事务、何时写入 journal、何时生成/写入 oplog、以及最终提交/刷盘】

本文 WiredTigerRecoveryUnit 入手的逐层、深入解析:包含主要数据结构、关键方法(开始/提交/回滚)、与 WiredTiger API 的映射、与 oplog/OpObserver 的交互、以及完整的执行伪代码和典型调用链(方便你在源码中精确定位追踪)。我会把重点放在“在一次写操作中,何时产生 WT 事务、何时写入 journal、何时生成/写入 oplog、以及最终提交/刷盘”的各个细节上。

说明:下面的函数名与类名基于 MongoDB 源码常见命名(如 WiredTigerRecoveryUnitWiredTigerKVEngineWiredTigerRecordStoreOpObserverImpl 等)。我会尽量把实现逻辑以伪代码 + 调用链的形式呈现,便于把说明与源码行数一一对应追踪


一、核心概念与主要对象(先梳理清楚)

  • RecoveryUnit(RU):MongoDB 内部用于管理单次操作/事务与存储引擎事务边界的抽象。WiredTigerRecoveryUnit 是其 WiredTiger 实现。每个 OperationContext(一次 client 请求上下文)通常会持有一个 RU。

    • 负责:开始/提交/回滚 WT 事务、注册 Change 回调(用于在 commit/abort 时触发回调)、管理 snapshot(读一致性)以及跟踪 in-memory 临时变更。
  • WT_SESSION(session):WiredTiger 的会话对象,承载 txn_begin/txn_commit/txn_rollbackWiredTigerRecoveryUnit 内部持有或借用一个 WT_SESSION *(或者 session 的包装)以执行底层 API。

  • WiredTigerKVEngine / WiredTigerRecordStore:KV 层负责把 MongoDB 操作映射到 WiredTiger 的表/索引 insert/update/delete 调用,实际向 WiredTiger 发起底层修改。

  • OpObserver / oplog:在执行写操作时,MongoDB 的业务层会通过 OpObserver 在合适时机向 local.oplog.rs 插入一条 oplog entry。为保证原子性(数据变更与对应 oplog 要么同时生效,要么都不生效),MongoDB 把 oplog 插入与数据变更放入同一个 RU / WT 事务内提交。

  • WiredTiger WAL / journal:WT 维护自己的 log buffer 与 journal 文件(MongoDB 中 dbPath/journal/WiredTigerLog.*)。当 WT_SESSION->txn_commit 被调用时,WT 会把对应的 log record 写进其内存缓冲(并根据配置做 group-commit / flush)。j:true(客户端请求要求 journaling)会触发更强的持久化保证(触发 fsync/journal flush)。

  • Timestamp 机制(MongoDB 的时间戳支持):MongoDB 在支持多文档事务和快照读时,会在 RU/WT 中管理 commitTimestamp、stableTimestamp、oldestTimestamp 等(由 WiredTigerKVEngine 与 replication 层协同维护),用于确定哪些数据对读者可见以及 checkpoint 时间点。这个机制影响 checkpoint/rollback 和可见性,但主要逻辑在 WiredTigerKVEngine/TimestampMonitor 中。


二、核心字段(WiredTigerRecoveryUnit 典型成员)

(便于你在源码中快速识别)

  • _session / _sessionCacheEntry:包装好的 WT_SESSION。
  • _active / _inUnitOfWork:标记是否已开始一个 WT 事务 / 单元工作。
  • _commitTimestamp / _prepareTimestamp:如果在事务或 prepare 情况下,会记录相关时间戳(仅在支持事务时)。
  • _changes:在 RU 上注册的 Change 回调集合(commit/abort 时触发)。
  • _roundUpToStableTimestamp / _durableTimestamp(与 timestamp 管理相关)。
  • _prepared:标记事务是否处于 prepare 状态(multi-doc transactions)。
  • _hasUncommittedOps:表示 RU 内是否有实际写操作(影响是否需要触发 WT 的提交逻辑)。

三、典型的写入执行流程(端到端,含关键函数与伪代码)

下面给出完整的高层流程(从接收到写请求到向客户端返回),随后展开 RU 层的详细伪代码。

  1. Client → mongod 接收写请求(例如 insert/update/delete)
  2. OperationContext 中创建/获取 WiredTigerRecoveryUnit(RU)
  3. 业务层(CRUD 操作)调用 record store 的 insert/update 等(这些调用使用当前 RU 的 WT_SESSION 执行 WT 操作)
  4. OpObserver(或 transaction commit 逻辑)构造并把 oplog 插入 local.oplog.rs(这一步也使用相同 RU)
  5. RU 提交:WiredTigerRecoveryUnit::commitUnitOfWork() 调用 WT_SESSION->txn_commit 或等价包装
  6. WiredTiger 把事务的 log record 写入 log buffer;根据配置(group-commit / j:true)触发 sync 到 journal 或等待下一次 group commit
  7. Replication 层处理 writeConcern(例如等待 majority ack 或等待 journal flush),最终向 client 返回
  8. 后台线程会定期 checkpoint,把脏页写回数据文件;journal 中的记录帮助 crash-recovery 时回放(checkpoint + replay journal)

RU 层(WiredTigerRecoveryUnit)的伪代码(最关键路径)

class WiredTigerRecoveryUnit : public RecoveryUnit {
  WT_SESSION* _session;
  bool _inUnitOfWork = false;
  vector<Change*> _changes;
  Timestamp _commitTimestamp;
  bool _hasWrites = false;

  // Called when operation needs to start a storage transaction
  void beginUnitOfWork() {
    if (!_inUnitOfWork) {
      _session = _sessionCache->getSession();         // get/checkout WT_SESSION
      // begin a WT transaction with possible snapshot/timestamp settings
      // e.g., WT_SESSION->txn_begin(_session, NULL);
      wt_txn_begin(_session, _beginConfig);
      _inUnitOfWork = true;
    }
  }

  // Called by record-store writes to mark that writes happened
  void markHasWritten() { _hasWrites = true; }

  // registerChange: push Change objects that have commit/rollback callbacks
  void registerChange(Change* change) {
    _changes.push_back(change);
  }

  // Commit UI (called at end of OperationContext or UnitOfWork)
  void commitUnitOfWork() {
    if (!_inUnitOfWork) return;

    // 1) Run pre-commit hooks? (some metadata ops)
    for (Change* c : _changes) c->preCommit();

    // 2) If using prepare / transaction timestamps set them (for multi-doc txns)
    if (_commitTimestamp) {
      // set WT transaction timestamp via WT extensions (if enabled)
      wt_txn_set_commit_timestamp(_session, _commitTimestamp);
    }

    // 3) WT commit (this writes log record to WT log buffer)
    int ret = WT_SESSION->txn_commit(_session, NULL);
    if (ret != 0) { // handle error -> run abort callbacks below
      for (Change* c : reverse(_changes)) c->rollback();
      _sessionCache->releaseSession(_session);
      _inUnitOfWork = false;
      throw Exception;
    }

    // 4) Run commit callbacks
    for (Change* c : _changes) c->commit();

    // 5) release session back to pool
    _sessionCache->releaseSession(_session);
    _inUnitOfWork = false;
    _changes.clear();
    _hasWrites = false;
  }

  // Rollback
  void abortUnitOfWork() {
    if (!_inUnitOfWork) return;
    WT_SESSION->txn_rollback(_session, NULL);
    for (Change* c : reverse(_changes)) c->rollback();
    _sessionCache->releaseSession(_session);
    _inUnitOfWork = false;
    _changes.clear();
    _hasWrites = false;
  }
}

说明与要点

  • WT_SESSION->txn_commit 是关键:它把该事务的修改写入 WT 的 log buffer,并将事务标记为已提交(对 WT/其他 sessions 可见)。但这不必然意味着数据已被 fsync 到磁盘(取决于 group-commit / j:true / commitIntervalMs 设置)。
  • Change 接口用于注册在 commit/rollback 时需要执行的额外逻辑(例如事务日志里生成后置操作、变更 Index 的元数据、发布 OpObserver 回调等)。OpObserver 的一些工作会通过 Change 注册在 RU 上,以便在 commit 时执行真实的副作用(或回退时撤销)。
  • 如果在 commit 过程中发生错误,RU 必须回滚 WT 事务并触发已注册的 rollback() 回调,以保持系统一致性。

四、oplog 的写入如何与 RU/WT 结合(保证原子性与可复制性)

关键原则:oplog 插入 与 数据修改 在同一 WT 事务中。实现上通常如下:

  1. 应用层开始 RU(beginUnitOfWork())。

  2. 执行 collection 的数据操作(insert/update/delete),这些操作由 WiredTigerRecordStore 使用 RU 的 _session 发起 WT 操作。

  3. 在写操作结束前,业务层生成一个 oplog document(包含 op type、ns、o、o2、ts、wall、txnNumber 等字段),并通过 OpObserver 写入 local.oplog.rs

    • OpObserverImpl::onInserts / onUpdate 等会在合适时机调用 repl::logOp 或直接 insert 到 oplog collection。
    • 重要:oplog 插入也使用当前 RU 的 session 执行底层 insert(因此它会成为同一 WT 事务的一部分)。
  4. 最终调用 commitUnitOfWork(),触发 WT 的 txn_commit,使得数据变更与 oplog 插入同时“提交”。

这样确保:要么 primary 的数据与对应的 oplog 同时可见(对 secondary 可复制),要么都不生效(例如在 crash 或 abort 的情况下)。


五、j:true(写入请求要求 journaling)如何影响路径

  • 当客户端请求 j:true

    • MongoDB 在确认写已被持久化前不会回应客户端。
    • 实现上,WiredTigerRecoveryUnit::commitWT_SESSION->txn_commit 返回后,需要确保 WT 日志已 flush 到磁盘(journal)
    • WiredTiger 自身支持 group-commit。WT_SESSION->txn_commit 会把 log record 写到内存,若请求了显式 sync,MongoDB 会调用 WT 的 log->sync 或依赖 WT 的 log_write 参数。MongoDB 会根据配置触发 fsync 或使用 WT 提供的日志 flush 接口,以满足 j:true 的要求。
    • 这通常通过 WiredTigerKVEngine::logFlush() 或类似封装函数来实现(在源码中搜索 journalwaitForJournalFlush 可找到具体实现)。

六、与 timestamp / snapshot 的关系(高级 — 事务与稳定性)

MongoDB 的时间戳机制(用于支持快照读和 multi-document transactions)会与 RU 紧密配合:

  • 在 prepare/commit 情形下,RU 会在提交前设置 WT 事务的 commit timestamp(通过 WT 的扩展 API),使得该事务的 visibility 与磁盘上的 durable timestamp 协调。
  • WiredTigerKVEngine 维护 stable_timestampoldest_timestamp,并定期向 WT 通知这些时间点,以便 checkpoint 不会丢弃仍需用于读一致性的历史版本。
  • 这套机制确保:读者持有某个 snapshot 时间戳时,WT 中对应版本的数据仍可以被保留直到 checkpoint 之后(否则会影响 snapshot reads)。

(源码线索:WiredTigerKVEngine::setStableTimestampWiredTigerRecoveryUnit::setCommitTimestamp 等;在 src/mongo/db/storage/wiredtiger/ 下可找到。)


七、崩溃恢复流程(使用 journal + checkpoint)

  1. MongoDB 正常运行时,WT 周期性 checkpoint(把数据页写入数据文件)并产生 checkpoint timestamp。

  2. WT 的 journal 存放 checkpoint 之后到当前的 log changes(log records),这些 journal 文件位于 dbPath/journal 下。

  3. 若 mongod 非正常崩溃启动恢复:

    • WiredTiger 会先加载最近的 checkpoint(数据文件),然后 replay journal 中的 log records,把变更应用到数据文件,直到达到最后的已提交点(最后的 txn_commit)。
    • replay 时也会校验 log record 的校验和并应用。
  4. 恢复完成后,mongod 进入正常服务流程。oplog 的条目(在 local.oplog.rs)会继续作为复制/恢复的参考(在 replica set 场景中,Secondary 可能会基于 oplog 进一步 catch-up)。


八、典型的调用链(写操作到 commit —— 以 insert 为例)

在这里插入图片描述
在这里插入图片描述


九、源码定位提示(马上就能打开的位置索引)

在 MongoDB 源码仓库中(src/mongo/db/storage/wiredtiger/)重点查看以下文件(文件名基于常见 repository 布局):

  • wiredtiger_recovery_unit.h / wiredtiger_recovery_unit.cpp:RU 的声明与实现(开始/提交/回滚、Change 管理、与 WT API 交互)。
  • wiredtiger_kv_engine.cpp:初始化 WT 引擎时拼接 wiredtiger_open 的 config(其中包含 log=(enabled=true,path=journal,compressor=snappy) 等),以及 timestamp/flush/backup 相关接口。
  • wiredtiger_record_store.cpp:collection-level insert/update/delete 的底层实现(如何调用 WT_CURSOR/WT_SESSION)。
  • repl/oplog.cpprepl/replication_coordinator / oplog.cpp:构建并插入 oplog 的逻辑(OpObserver 触发点)。
  • storage/wiredtiger/wt_session_cache.*:session 池的实现(如何复用 WT_SESSION)。
  • wt 工具与 WiredTiger 源码(如果要看 WT 的日志格式/record header/txn_commit 实际效果)在 WiredTiger 项目中(例如 log 目录下的实现)。
posted @ 2025-09-28 15:20  NeoLshu  阅读(15)  评论(0)    收藏  举报  来源