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

MongoDB 日志/记录 机制源码级详解【WiredTiger 执行流程深度分析】

本文 MongoDB 的“日志/记录”机制(包括 WiredTiger 的 WAL/journal、MongoDB 的 oplog、mongod 的结构化日志、FTDC 等)做成一份深入讲解:原理、逐步执行流程(写入/复制/恢复)、以及关键源码位置与可检索命令。下面按主题展开,尽量把执行流程和源码位置都标注清楚,方便读者跟进源码阅读或做调试。

说明:我在正文中用到了官方文档与源码(GitHub / WiredTiger / MongoDB docs),重要结论后都放了来源链接,想看源码可以直接点开对应条目。(MongoDB)


一、先把“什么是日志”搞清楚(分类与作用)

  1. mongod 的运行日志(server log)

    • 进程级的结构化 JSON 日志(startup、INFO/WARN/ERROR、组件字段如 STORAGE/REPL/WT 等),用于运维与故障诊断,可通过 getLog、log 文件或 syslog 获取。(MongoDB)
  2. FTDC(Full-Time Diagnostic Data Capture)/diagnostic.data

    • 周期性收集性能与诊断指标的二进制快照,便于长期问题分析/支持请求。(MongoDB)
  3. WiredTiger 的 Journal(write-ahead log,WAL) —— 存储层的“持久化保证”

    • WiredTiger 为每个“客户端发起的写操作”产生一个 journal 记录(包括该操作导致的所有内部写,比如索引更新)。WiredTiger 结合 checkpoint + journal 提供 crash-recovery。journal 文件放在 dbPath/journal,命名类似 WiredTigerLog.0000000001。默认 group-commit 间隔为 100msstorage.journal.commitIntervalMs),并在 ~100MB 换新 journal 文件。(MongoDB)
  4. Oplog(operation log,local.oplog.rs) —— 复制层的“变更流”

    • Replica Set 的 primary 将对外数据变更以 oplog 条目的形式写入 local.oplog.rs(一个 capped collection),其他成员拉取并应用这些条目以达成副本同步。oplog 与 storage 层的 journal 目的不同:oplog 用于复制/重放、journal 用于 crash recovery。(MongoDB)

二、WiredTiger 日志(WAL / Journal)深度解析 —— 概念 + 行为

关键事实(重要到位):

  • 一个 client 写操作 → 一个 journal 记录(该记录可以包含多个底层修改,例如主文档写入 + 索引修改)。这保证了单次操作的内部修改能作为原子单元被重放。(MongoDB)
  • Group-commit / flush 触发条件:默认每 storage.journal.commitIntervalMs(WiredTiger 默认 100ms)做一次 group commit;同时在出现 j:true(驱动/写关注)或触发新 journal 文件(~100MB)时会导致更立即的 sync。也可以调 storage.journal.commitIntervalMs(范围 1–500 ms)。(MongoDB)
  • Checkpoint 与 journal 配合:WiredTiger 定期 checkpoint(将脏页写回数据文件),journal 保存 checkpoint 之后到最新提交之间的修改;崩溃恢复时会加载 checkpoint 再 replay journal。(MongoDB)

内部结构(High-level)

  • WiredTiger 的日志记录(在 WiredTiger 源与文档中以 WT_LOG_RECORD / log record 描述)包含:记录头(标识、类型、校验)、事务边界信息、payload(修改内容/serialized changes),以及压缩(默认 MongoDB 用 snappy)和校验。你可以用 wt 工具查看/分析 WT 文件。(WiredTiger)

MongoDB 如何配置 WiredTiger 的 journal

  • 在 MongoDB 启动时,WiredTigerKVEngine 会把 WiredTiger 的配置字符串(包括 log=(enabled=..., path=journal, compressor=...))拼接并传给 wiredtiger_open,因此有专门的代码控制是否打开日志、压缩器等(源码位置我后面列出)。(GitHub)

三、写入执行流程(单条写入,Primary 情形) — 逐步(带源码位置提示)

以下流程以现代版本(WiredTiger 为存储引擎、Replica Set 环境)为准,强调“何时写入 journal / oplog / 数据”,并标注 MongoDB 源码中负责的关键模块/类(便于你追踪实现)。

  1. Client 发起写请求(driver → mongod)

    • driver 可能带有 writeConcern(如 w:1/w:majority),也可能带 j:true 要求 journaling 持久化确认。(MongoDB)
  2. mongod 验证权限 & 进入写上下文

    • 调用存储层的写入入口(生成 OperationContext、打开写入事务)。在 WiredTiger 情况下,会创建或使用 WiredTigerRecoveryUnit(MongoDB 与 WT 交互的 Recovery Unit)开始一个 WT 事务。查看:src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.*。(GitHub)
  3. 构建并写入数据与索引变更

    • 所有集合/索引的底层修改会在当前 WT 事务内被执行(调用 WiredTigerRecordStore::insert/update/... 等)。查看:src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp(record-store 层负责把 BSON 转为底层 key/value 并写入 WT)。(GitHub)
  4. 生成并写入 Oplog 条目(在 primary)

    • 在写操作逻辑层,MongoDB 会通过 OpObserver 系列(OpObserverImpl 等)在适当时机写入 oplog 条目(local.oplog.rs)。重要:为了保持主从一致性,MongoDB 把 oplog 插入数据变更 放在同一个存储引擎事务里(即同一 WT 事务),这样数据与对应的 oplog 条目要么一起成功,要么都不生效(保证原子性/顺序)。源码相关位置在 src/mongo/db/repl/(例如 oplog.cpp 及 opObserver 的实现)。(GitHub)

    • (注)对于 multi-document transaction(4.0+),提交时会在 oplog 生成一个事务提交的单条表示条目(transaction commit entry);内部操作会以合适方式表示到 oplog。(MongoDB)

  5. 提交 WT 事务(WiredTigerCommit)

    • RecoveryUnit(WiredTigerRecoveryUnit)提交事务,WiredTiger 内部把该事务产生的日志记录写入其 log buffer,并根据配置做 group commit 或立即刷盘(例如 j:true 会触发更快的 sync)。提交到 WT 表示该事务已进入 WT 的可恢复状态(但不一定已经 fsync 到物理盘,除非刷盘发生)。查看 WiredTigerRecoveryUnit / WiredTigerKVEngine 的实现(源码路径如上)。(GitHub)
  6. 向客户端返回(由 writeConcern 决定何时)

    • 如果写关注是 w:1 且未要求 j:true,mongod 在本地提交后就可以应答。
    • 如果 j:truewriteConcern: { w: "majority" }(在多数副本ACK的语义下,也通常隐含 journaling 要求),mongod 会等待相应的 fsync/journal flush 或等待其他副本 ACK(具体逻辑由 replication/write concern 层处理)。官方文档说明 w:majority 与 journaling 的关系与等待行为。(MongoDB)
  7. WiredTiger group-commit / checkpoint(后台)

    • WiredTiger 定期(默认 100ms)将 log buffer group commit 到 journal 文件;并且后台 checkpoint(默认每 ~60s,数据库可自定义)把脏页写回数据文件,缩小恢复时间窗口。崩溃恢复时,先加载 checkpoint 再 replay journal。(MongoDB)

四、Replica Set 上的 Oplog 与复制流程(Secondary 是如何应用的)

  1. Primary:如上,在提交时把 oplog 条目一起写入 local.oplog.rs(同一 WT 事务)→ 其他副本可见该 oplog 条目。文档说明了 oplog 的 role/格式与大小管理。(MongoDB)

  2. Secondaries(拉取并应用)

    • Secondaries 通过复制握手拉取 primary 的 oplog(tailing),按顺序把这些 oplog 条目分发到 apply 线程组(MongoDB 会对不同 document id 分组以并行应用),并在本地应用变更。Secondary 不会为这些远程操作再次写入自己的 oplog(它复制并应用已有的 oplog)。应用过程会在 storage engine 上产生本地 WT 事务并提交(因此 secondaries 也会有 journal/commit 行为以保证本地 crash-recovery)。(MongoDB)
  3. 一致性保证

    • w:majority 的语义与 oplog 的应用、选主点(majority commit point)紧密相关;MongoDB 保证写入一旦被多数成员确认即视为被持久化(多数成员各自通过自己的 journal/checkpoint 达到一致)。详细语义可参考 docs(Write Concern + replica set 同步文档)。(MongoDB)

五、WiredTiger 日志(WT)内部与实用工具(快速参考)

  • WiredTiger 日志结构、记录头、校验与压缩等可在 WiredTiger 文档与源码看到(WT_LOG_RECORD 概念)。wt 命令行工具可以对 .wt 文件和日志做分析(wt -h /path dumpwt readlog / wt log 等命令,具体命令在 WiredTiger 文档里)。(WiredTiger)

  • forensics / 调试建议:Percona 等有文章讲如何用 wt dumpwt readlog 等工具在 data 文件或 journal 丢失/损坏时做取证。(Percona)


六、关键源码位置(直接链接 + 简短说明,方便你逐个打开读)

我把最相关 / 最常被引用的实现文件列出来(源码以 mongodb/mongo 仓库为准),你可以直接跳到这些文件看实现细节。

  • src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp
    — WiredTiger 引擎初始化与配置(拼接 wiredtiger_open config,包括 log=(enabled=...,path=journal,compressor=...)),控制是否启用 journal、压缩器等。(GitHub)

  • src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
    — record-store 层,把 MongoDB 的 BSON/collections 操作转为 WiredTiger 的底层插入/删除/更新。(GitHub)

  • src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.h / .cpp
    — RecoveryUnit 的 WiredTiger 实现(管理 WT 事务的开始/提交/回滚、与 MongoDB 的事务上下文衔接)。查看 WiredTigerRecoveryUnit 能帮助你理解何时调用 WT->txn_commit。(GitHub)

  • src/mongo/db/repl/oplog.cpp(及 src/mongo/db/op_observer/*
    — 构造与写入 oplog 的逻辑、以及复制相关的 helper。你可以在这里看到 oplog 条目是如何被构造、序列化并插入到 local.oplog.rs 的。(GitHub)

  • WiredTiger 源码(log 实现)
    wiredtiger 仓库里有 log write/read、record header 定义等(例如 log 目录与实现)。若你要看 WT 的底层 WAL 格式,这里是源头。(GitHub)


七、实操命令(调试/排查时常用)

  • 查看 mongod 日志(JSON):/var/log/mongodb/mongod.logjournalctl -u mongod(取决于你的部署);也可用 db.adminCommand({ getLog: "global" }) 从内存读取最近日志。(MongoDB)
  • 查看 WiredTiger journal 统计:db.serverStatus().wiredTiger.log(其中有 log->write, log->writeToLog 等统计项)。(MongoDB)
  • 在数据目录查看 journal 文件:ls -lh /path/to/db/journal(文件名 WiredTigerLog.*)。(MongoDB)
  • 使用 wt 查看/分析 WiredTiger 文件(示例):wt -h /path/to/db dump table:collection-<ident>wt -h /path/to/db readlog(参考 WiredTiger 文档)。(WiredTiger)

八、常见误区 / 注意点(实务经验)

  • 别把 oplog 与 storage journal 混淆:oplog 用于复制;journal 用于 crash-recovery。同一写请求通常会同时写入两者,但用途与生命周期不同。(Stack Overflow)
  • 默认 100ms 的窗口不是无限可接受:如果你需要更高的持久化保证,使用 j:true 或更小的 storage.journal.commitIntervalMs,同时注意这会降低吞吐/增加 IO。(MongoDB)
  • WiredTiger 总是会用 journal + checkpoint 恢复:即使启用了 WiredTiger,若 mongod 在 checkpoint 之间崩溃,journal 用于把数据恢复到最后提交点。(MongoDB)

九、如果你想更“动手”看源码 / 我可以接着做的事

我已经把关键文档和源码位置都贴在上面(点开即可阅读)。如果你想我进一步做任一项,我可以直接在当前对话里替你做:

  • 把某个源码文件(例如 wiredtiger_recovery_unit.*oplog.cpp逐段解析并标注关键行号与执行路径(把关键函数的伪代码/流程图写出来);或
  • 把 “写入 + oplog + journal” 的代码执行路径做成一份可打印的调用栈式流程(包含函数名/调用链/条件分支);或
  • 针对某个具体问题(比如“为何在 crash 后丢失 X 秒的写入”或“如何用 wt 工具导出 oplog 影响的底层 data”)给出实操步骤和命令。

(你要哪一个,我就直接开始把对应源码段落按行解释并粘出要点 — 不用你再确认,我会直接把我能做好的内容发出来。)


参考(主来源)

  • MongoDB — Journaling / WiredTiger storage engine(官方文档)。(MongoDB)
  • MongoDB — Replica set oplog & replication 文档。(MongoDB)
  • MongoDB 源码(wiredtiger_kv_engine.cpp、wiredtiger_record_store.cpp、wiredtiger_recovery_unit.h、repl/oplog.cpp)。(GitHub)
  • WiredTiger 官方文档与 wt 工具说明(log/读写工具、WT 日志结构)。(WiredTiger)
  • Percona / 博文(WiredTiger file forensics、wt dump 等实践指南)。(Percona)
posted @ 2025-09-28 15:03  NeoLshu  阅读(13)  评论(0)    收藏  举报  来源