MongoDB 日志/记录 机制源码级详解【WiredTiger 执行流程深度分析】
本文 MongoDB 的“日志/记录”机制(包括 WiredTiger 的 WAL/journal、MongoDB 的 oplog、mongod 的结构化日志、FTDC 等)做成一份深入讲解:原理、逐步执行流程(写入/复制/恢复)、以及关键源码位置与可检索命令。下面按主题展开,尽量把执行流程和源码位置都标注清楚,方便读者跟进源码阅读或做调试。
说明:我在正文中用到了官方文档与源码(GitHub / WiredTiger / MongoDB docs),重要结论后都放了来源链接,想看源码可以直接点开对应条目。(MongoDB)
一、先把“什么是日志”搞清楚(分类与作用)
-
mongod 的运行日志(server log)
- 进程级的结构化 JSON 日志(startup、INFO/WARN/ERROR、组件字段如 STORAGE/REPL/WT 等),用于运维与故障诊断,可通过
getLog、log 文件或 syslog 获取。(MongoDB)
- 进程级的结构化 JSON 日志(startup、INFO/WARN/ERROR、组件字段如 STORAGE/REPL/WT 等),用于运维与故障诊断,可通过
-
FTDC(Full-Time Diagnostic Data Capture)/diagnostic.data
- 周期性收集性能与诊断指标的二进制快照,便于长期问题分析/支持请求。(MongoDB)
-
WiredTiger 的 Journal(write-ahead log,WAL) —— 存储层的“持久化保证”
- WiredTiger 为每个“客户端发起的写操作”产生一个 journal 记录(包括该操作导致的所有内部写,比如索引更新)。WiredTiger 结合 checkpoint + journal 提供 crash-recovery。journal 文件放在
dbPath/journal,命名类似WiredTigerLog.0000000001。默认 group-commit 间隔为 100ms(storage.journal.commitIntervalMs),并在 ~100MB 换新 journal 文件。(MongoDB)
- WiredTiger 为每个“客户端发起的写操作”产生一个 journal 记录(包括该操作导致的所有内部写,比如索引更新)。WiredTiger 结合 checkpoint + journal 提供 crash-recovery。journal 文件放在
-
Oplog(operation log,local.oplog.rs) —— 复制层的“变更流”
- Replica Set 的 primary 将对外数据变更以 oplog 条目的形式写入
local.oplog.rs(一个 capped collection),其他成员拉取并应用这些条目以达成副本同步。oplog 与 storage 层的 journal 目的不同:oplog 用于复制/重放、journal 用于 crash recovery。(MongoDB)
- Replica Set 的 primary 将对外数据变更以 oplog 条目的形式写入
二、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 源码中负责的关键模块/类(便于你追踪实现)。
-
Client 发起写请求(driver → mongod)
- driver 可能带有
writeConcern(如w:1/w:majority),也可能带j:true要求 journaling 持久化确认。(MongoDB)
- driver 可能带有
-
mongod 验证权限 & 进入写上下文
- 调用存储层的写入入口(生成
OperationContext、打开写入事务)。在 WiredTiger 情况下,会创建或使用WiredTigerRecoveryUnit(MongoDB 与 WT 交互的 Recovery Unit)开始一个 WT 事务。查看:src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.*。(GitHub)
- 调用存储层的写入入口(生成
-
构建并写入数据与索引变更
- 所有集合/索引的底层修改会在当前 WT 事务内被执行(调用
WiredTigerRecordStore::insert/update/...等)。查看:src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp(record-store 层负责把 BSON 转为底层 key/value 并写入 WT)。(GitHub)
- 所有集合/索引的底层修改会在当前 WT 事务内被执行(调用
-
生成并写入 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)
-
-
提交 WT 事务(WiredTigerCommit)
- RecoveryUnit(WiredTigerRecoveryUnit)提交事务,WiredTiger 内部把该事务产生的日志记录写入其 log buffer,并根据配置做 group commit 或立即刷盘(例如
j:true会触发更快的 sync)。提交到 WT 表示该事务已进入 WT 的可恢复状态(但不一定已经 fsync 到物理盘,除非刷盘发生)。查看WiredTigerRecoveryUnit/WiredTigerKVEngine的实现(源码路径如上)。(GitHub)
- RecoveryUnit(WiredTigerRecoveryUnit)提交事务,WiredTiger 内部把该事务产生的日志记录写入其 log buffer,并根据配置做 group commit 或立即刷盘(例如
-
向客户端返回(由 writeConcern 决定何时)
- 如果写关注是
w:1且未要求j:true,mongod 在本地提交后就可以应答。 - 如果
j:true或writeConcern: { w: "majority" }(在多数副本ACK的语义下,也通常隐含 journaling 要求),mongod 会等待相应的 fsync/journal flush 或等待其他副本 ACK(具体逻辑由 replication/write concern 层处理)。官方文档说明w:majority与 journaling 的关系与等待行为。(MongoDB)
- 如果写关注是
-
WiredTiger group-commit / checkpoint(后台)
- WiredTiger 定期(默认 100ms)将 log buffer group commit 到 journal 文件;并且后台 checkpoint(默认每 ~60s,数据库可自定义)把脏页写回数据文件,缩小恢复时间窗口。崩溃恢复时,先加载 checkpoint 再 replay journal。(MongoDB)
四、Replica Set 上的 Oplog 与复制流程(Secondary 是如何应用的)
-
Primary:如上,在提交时把 oplog 条目一起写入 local.oplog.rs(同一 WT 事务)→ 其他副本可见该 oplog 条目。文档说明了 oplog 的 role/格式与大小管理。(MongoDB)
-
Secondaries(拉取并应用):
- Secondaries 通过复制握手拉取 primary 的 oplog(tailing),按顺序把这些 oplog 条目分发到 apply 线程组(MongoDB 会对不同 document id 分组以并行应用),并在本地应用变更。Secondary 不会为这些远程操作再次写入自己的 oplog(它复制并应用已有的 oplog)。应用过程会在 storage engine 上产生本地 WT 事务并提交(因此 secondaries 也会有 journal/commit 行为以保证本地 crash-recovery)。(MongoDB)
-
一致性保证:
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 dump、wt readlog/wt log等命令,具体命令在 WiredTiger 文档里)。(WiredTiger) -
forensics / 调试建议:Percona 等有文章讲如何用
wt dump、wt readlog等工具在 data 文件或 journal 丢失/损坏时做取证。(Percona)
六、关键源码位置(直接链接 + 简短说明,方便你逐个打开读)
我把最相关 / 最常被引用的实现文件列出来(源码以 mongodb/mongo 仓库为准),你可以直接跳到这些文件看实现细节。
-
src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp
— WiredTiger 引擎初始化与配置(拼接wiredtiger_openconfig,包括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.log或journalctl -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)
本文来自博客园,作者:NeoLshu,转载请注明原文链接:https://www.cnblogs.com/neolshu/p/19120283

浙公网安备 33010602011771号