第33章:MongoDB WiredTiger 存储引擎——从 B-Tree 到缓存淘汰
1. 项目背景业务场景本地生活电商的 DBA 发现一个诡异的现象——高峰期 MongoDB 的磁盘 IOPS 飙升至 15000但数据写入量明明只有 5000 IOPS。多出来的 10000 IOPS 是哪里来的查看 WiredTiger 的统计指标后发现——pages evicted by application threads数量飙升意味着 WiredTiger 缓存放不下工作集频繁地把脏页刷到磁盘来腾空间给新数据而这些被淘汰的页很快又被查询读回来——造成了大量的读-淘汰-再读的磁盘抖动。还有一个隐藏成本——用了 snappy 压缩算法CPU 使用量比预期高了 30%而实际数据体积只节省了 20%。如果换用 zstd 压缩能多节省 15% 的空间但 CPU 再涨 20%——怎么权衡痛点WiredTiger 是 MongoDB 的内核但多数人对它只知其名。缓存怎么淘汰、B-Tree 怎么组织、Journal 怎么保证崩溃恢复、压缩算法怎么选——这些存储引擎的内部机制如果不理解就只能在 serverStatus 的数字面上看问题看不懂根因。2. 项目设计小胖看着 Grafana 里 WiredTiger 的 eviction 曲线大师缓存淘汰eviction是啥为什么缓存满了不直接报错而是要淘汰旧数据大师WiredTiger 的缓存就像你办公桌上的书架——桌面上只能放固定数量的书缓存大小当你需要看一本新书时如果桌面满了你得先拿走一本不常用的书淘汰。淘汰的策略是优先扔掉最久没用的LRU但如果那本书被修改过还没存回书库脏页需要先写回刷盘才能丢。技术映射WiredTiger 的缓存淘汰机制Clean Page 淘汰直接从内存中丢弃无 IO——因为数据在磁盘上已有最新副本。Dirty Page 淘汰必须先写入磁盘再丢弃产生 IO——因为内存中的版本比磁盘新。小胖那我看到的 10000 额外 IOPS 就是脏页淘汰产生的大师对。当你的工作集大于 WiredTiger 缓存时脏页淘汰成为主要磁盘 IO 来源——这叫工作集膨胀。监控的关键是wiredTiger.cache.tracked dirty bytes in the cache和pages evicted by application threads。如果 eviction rate 持续 0说明缓存不够用。小白B-Tree 呢MySQL 用 BTreeWiredTiger 也用 B-Tree有什么不同大师相同的都是平衡多路查找树——数据有序存储按页组织通过指针连接。WiredTiger 的 B-Tree 特色在于页内记录Row-store和列式存储Column-store两种格式——MongoDB 用 Row-store。页的大小默认为 4KB——比 InnoDB 的 16KB 小得多适应更多小文档的场景。磁盘镜像Disk Image和内存镜像In-Memory不同——磁盘上页是压缩的读到内存后才解压。所以 WiredTiger 的内存消耗 未压缩的页数量 × 4KB。技术映射WiredTiger 页 B-Tree 的节点。每个页在磁盘上是压缩存储的在内存中是未压缩的。缓存中同时存在 Clean只读不写和 Dirty已修改未刷盘两种页状态。小胖那 Checkpoint 是什么跟 MySQL 的 checkpoint 一样吗大师Checkpoint 是 WiredTiger 创建一致性快照的机制。每 60 秒默认或 Journal 文件达到 2GB 时触发一次。Checkpoint 做两件事——把所有脏页刷盘、更新元数据——使数据库进入一个新的一致性状态。崩溃恢复时WiredTiger 从最近的 Checkpoint 开始重放 Journal 日志恢复之后的操作。技术映射Checkpoint 是 WiredTiger 持久化的核心环节。Checkpoint 期间的 CPU 和磁盘 IO 会短时间内升高——这就是 MongoDB 每 60 秒会出现一个性能毛刺的根源。大师总结记住四组概念——B-Tree 是数据组织方式缓存淘汰是内存管理机制Journal 是崩溃恢复保障Checkpoint 是周期性持久化。四者构成 WiredTiger 的完整运转闭环。3. 项目实战3.1 环境准备使用 Docker MongoDB 并挂载自定义配置以调优 WiredTiger 参数。dockercompose-fmongodb-lab/docker-compose.ymlps3.2 分步实现步骤一查看 WiredTiger 核心指标// wt-stats.js —— 提取 WiredTiger 关键运行指标use adminvarsdb.serverStatus()print( WiredTiger 存储引擎指标 )// 1. 缓存varcaches.wiredTiger.cacheprint(缓存:)print( 配置最大: (cache[maximum bytes configured]/1024/1024/1024).toFixed(2) GB)print( 当前使用: (cache[bytes currently in the cache]/1024/1024/1024).toFixed(2) GB)print( 脏数据: ((cache[tracked dirty bytes in the cache]||0)/1024/1024).toFixed(0) MB)print( 使用率: (cache[bytes currently in the cache]/cache[maximum bytes configured]*100).toFixed(1)%)// 2. 页淘汰print(\n页淘汰:)print( 淘汰(应用线程): (cache[pages evicted by application threads]||0))print( 淘汰(后台线程): (cache[pages evicted by eviction server]||0))print( 因淘汰而刷脏: (cache[pages currently queued for eviction]||0))// 如果 application thread eviction 0缓存压力大——应用线程被迫介入淘汰// 3. Checkpointvarwts.wiredTigerprint(\nCheckpoint:)print( 耗时(ms): (wt[checkpoint][most recent time msecs]||N/A))print( 上次时间: (wt[checkpoint][last checkpoint]||N/A))// 4. 事务print(\n事务:)print( 事务冲突: (wt[transaction][transaction conflicts]||0))print( 写冲突: (wt[transaction][write conflicts]||0))// 5. 压缩print(\n压缩统计:)print( 页读入: (wt[block-manager][blocks read]||0))print( 页写出: (wt[block-manager][blocks written]||0))print( 预读: (wt[block-manager][blocks pre-loaded]||0))步骤二压测不同压缩算法目标对比 snappy、zlib、zstd 在不同场景下的表现。// compression-benchmark.js —— 压缩算法对比测试// 创建三个相同的集合分别用不同压缩算法// 注意集合的压缩算法在创建时指定通过 storageEngine 选项// 在 mongod 的配置文件中指定默认压缩// wiredTiger:// collectionConfig:// blockCompressor: snappy # 或 zlib / zstd / none// 或者在创建集合时指定db.createCollection(products_snappy,{storageEngine:{wiredTiger:{configString:block_compressorsnappy}}})db.createCollection(products_zstd,{storageEngine:{wiredTiger:{configString:block_compressorzstd}}})db.createCollection(products_none,{storageEngine:{wiredTiger:{configString:block_compressornone}}})// 插入相同的数据各 10 万条vartestDoc{name:压缩测试商品,description:这是描述.repeat(50)}for(varcollof[products_snappy,products_zstd,products_none]){vardocs[]for(vari0;i100000;i){docs.push(Object.assign({_id:i,price:Math.random()*1000},testDoc))if(docs.length5000){db[coll].insertMany(docs,{ordered:false})docs[]}}}// 对比三个集合的存储大小[products_snappy,products_zstd,products_none].forEach(function(c){varstatsdb[c].stats()print(c: (stats.storageSize/1024/1024).toFixed(1) MB (压缩(stats.storageSize/(stats.size||1)*100).toFixed(0)%))})// 结果解读// snappy: 压缩快CPU低、压缩率中等 → 适合高吞吐低延迟场景// zstd: 压缩率高CPU高、压缩慢 → 适合存储优先、读大于写的场景// none: 无压缩、CPU零开销 → 适合纯内存工作集数据远小于缓存大小步骤三缓存大小调优实验目标观察不同 cacheSizeGB 下的淘汰行为。# 在 mongod 启动参数中调整缓存大小# docker run ... mongo:8.0 --wiredTigerCacheSizeGB 1.0# 或修改 /etc/mongod.conf:# storage:# wiredTiger:# engineConfig:# cacheSizeGB: 1.0# 建议缓存在物理内存的 50%-70% 之间# 8GB 内存服务器cacheSizeGB 设为 4-5.5# 32GB 内存服务器cacheSizeGB 设为 16-22// 观察缓存压力指标是否改善varbeforedb.serverStatus().wiredTiger.cacheprint(调整前 eviction:,before[pages evicted by application threads])print(调整前 dirty:,(before[tracked dirty bytes in the cache]||0)/1024/1024,MB)// 调大 cacheSizeGB 后重新运行观察// 期望application thread eviction 降为 0dirty bytes 比例降低步骤四Checkpoint 行为观察与调优// checkpoint-monitor.js —— 观察 Checkpoint 的频率和影响// 查看 Checkpoint 配置varparamsdb.adminCommand({getParameter:*})print(Checkpoint 间隔(秒):,params[wiredTigerEngineRuntimeConfig].match(/checkpoint\(wait(\d)/)?.[1]||默认60)// checkpoint(wait60,log_size2GB) → 每 60 秒 或 Journal 达 2GB 时触发// 调优建议// 批量写入场景增大 log_size 以减少 checkpoint 频率// 低延迟场景减小 wait 以提高 checkpoint 频率减少单次脏页堆积量// 示例配置在 mongod.conf 中// storage:// wiredTiger:// engineConfig:// journalCompressor: snappy// checkpoint:// wait: 300 # 5 分钟// logSize: 4 # 4GB// 注意checkpoint 频率越低崩溃恢复时需要重放的 Journal 越多恢复时间越长步骤五JournalWAL机制理解// journal-explained.js —— Journal 是 WiredTiger 的预写日志// Journal 工作原理// 1. 写操作到达 → 先写入 Journal顺序写快// 2. 写入内存中的 B-Tree 页标记为 Dirty// 3. Checkpoint 周期性地将 Dirty 页刷入磁盘的数据文件// 4. 崩溃恢复时 → 读取最近的 Checkpoint 重放 Journal 恢复到崩溃前状态// 查看 Journal 统计varjournaldb.serverStatus().wiredTiger.logprint(Journal 写入(字节):,journal[total log buffer size bytes prepared]||N/A)print(Journal 同步:,journal[log sync operations]||N/A)// log sync operations 是 Journal 写入磁盘的次数// 频繁的 sync 意味着 writeConcern: jtrue 被大量使用// Journal 性能影响// - MongoDB 默认每 100ms 自动 sync 一次 Journal// - j:true 会在每次写入时立即 sync强制刷新到磁盘// - 对延迟敏感的写入接口避免使用 j:true除非需要绝对数据安全步骤六源码追踪——WiredTiger 缓存淘汰路径// 源码关键路径在 GDB 中追踪// 文件src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp// 函数调用链// WiredTigerRecordStore::insertRecord()// → WT_SESSION::insert() // 调用 WT 底层 API// → __wt_btree_insert() // B-Tree 插入// → __wt_cache_eviction() // 如果需要腾空间 → 淘汰// 在 GDB 中打断点// (gdb) break __wt_cache_eviction// 观察淘汰被触发的时机和淘汰的页面数量3.3 完整代码清单文件用途mongodb-lab/scripts/ch33-wt-stats.jsWiredTiger 指标提取mongodb-lab/scripts/ch33-compression-bench.js压缩算法对比mongodb-lab/scripts/ch33-cache-tuning.js缓存调优实验mongodb-lab/scripts/ch33-checkpoint-monitor.jsCheckpoint 观察mongodb-lab/config/mongod-wt-tune.confWiredTiger 调优配置模板3.4 测试验证use admin// 1. 缓存指标可读varcachedb.serverStatus().wiredTiger.cacheprint(缓存最大:,cache[maximum bytes configured]?PASS:FAIL)print(淘汰数:,cache[pages evicted]?PASS:FAIL)// 2. 压缩算法对比有效varcolls[products_snappy,products_zstd,products_none]colls.forEach(function(c){varsdb[c].stats()print(c 存储:,s.storageSize,bytes,s.storageSize0?PASS:FAIL)})// 3. Checkpoint 信息可查varwtdb.serverStatus().wiredTigerprint(Checkpoint:,wt.checkpoint?PASS:FAIL)print(\n WiredTiger 验证完成 )4. 项目总结4.1 WiredTiger 核心概念速查概念作用关键参数/指标B-Tree 页数据存储的最小单元默认 4KB/页内存中未压缩缓存Cache工作内存中缓存的 B-Tree 页cacheSizeGB默认 RAM×50%-1GB脏页淘汰腾出内存给新数据pages evicted by application threadsCheckpoint周期性持久化一致性快照默认 60s 或 Journal 2GB 触发JournalWAL崩溃恢复的预写日志默认 100ms sync 一次压缩算法磁盘数据压缩snappy快/ zstd高压缩率/ none4.2 适用场景WiredTiger 调优适用写入密集型应用——cacheSizeGB 和工作集匹配度是关键。存储成本敏感——用 zstd 压缩最大化磁盘利用率。延迟敏感——调小 checkpoint 间隔避免毛刺用 snappy 降低 CPU。崩溃恢复时间敏感——调小 checkpoint 间隔减少 Journal 重放量。4.3 注意事项注意事项说明cacheSizeGB 不能等于物理内存留至少 1-2GB 给操作系统和文件系统缓存压缩算法创建后不可更改只能在新建集合时指定或通过 reshard 重建Checkpoint 期间性能毛刺大量脏页刷盘的 IO 高峰是正常的eviction 0 不一定坏事后台 eviction server 正常淘汰 应用线程被迫淘汰才是问题4.4 常见踩坑经验故障案例一cacheSizeGB 设太小导致写入卡死某团队在 64GB 服务器上设了cacheSizeGB: 2写入高峰期 95% 的请求在等待 eviction 完成——应用线程被迫停在__wt_cache_eviction中等待脏页刷盘P99 延迟 30 秒。解决cacheSizeGB调到 40GB内存的 60%eviction 几乎消失。故障案例二zlib 压缩导致 CPU 爆满某大数据团队对所有集合用了block_compressorzlib日均写入 5 亿条日志时 CPU 100%数据压缩率确实高30%但写入吞吐下降 70%。解决日志集合换用 snappyCPU 省 50%压缩率仍有 20%核心交易集合用 zstd。故障案例三透明大页未禁用导致内存碎片Linux 默认开启透明大页THPWiredTiger 的 4KB 页与 2MB 大页冲突——内存碎片严重、bytes currently in the cache远小于 cacheSizeGB 但 eviction 已发生。解决echo never /sys/kernel/mm/transparent_hugepage/enabledMongoDB 官方强烈建议禁用 THP。4.5 思考题如果 WiredTiger 缓存的 80% 都是脏页会有什么后果为什么 MongoDB 默认把脏页比例控制在 20% 以内writeConcern: {j:true}在 WiredTiger 层面实际做了什么操作为什么 j:true 比 w:1 慢 10-100 倍答案将在第 34 章末尾揭晓上一章思考题答案GDB 中打印std::vectorBSONObj用p vec.size()看大小p vec[0]看第一个元素。打印前 5 个用循环或 GDB Python 脚本。大 vector 可用p vec._M_impl._M_start5打印前 5 个元素的原始指针。MONGO_COMPILER_NOINLINE等宏用于精细控制编译器的内联和优化行为——在性能关键路径上过度内联会导致代码膨胀和指令缓存失效NOINLINE强制函数保持独立在调试模式下被内联的函数无法在 GDB 中打断点所以调试版本用--optoff禁用内联。延伸阅读与资源MongoDB 实战进阶与内核修炼python入门Rquests从菜鸟脚本到企业级SDK的网络实战圣经Milvus向量数据库实战修炼从 0 到 1精通向量检索与生产落地后端工程师的 AI 转型第一课Ollama 与私有化大模型实战10倍开发者的 Dify 魔法书从零构建全栈 AI 应用后端工程师转型AI第一课-Ollama 与私有化大模型实战大型语言模型(LLM) vLLM 高性能推理落地实战Agent开发之LlamaIndex 实战修炼与源码进阶大语言模型Transformers 实战修炼与源码剖析

相关新闻

三大财务报表还不会看?资产、利润、现金流的分析顺序一次讲清

三大财务报表还不会看?资产、利润、现金流的分析顺序一次讲清

很多企业每个月都会出资产负债表、利润表和现金流量表,但管理层拿到报表后,往往只关注收入、净利润和银行余额。 结果就是: 利润增长了,却不知道钱为什么没有回来; 现金增加了,却分不清是经营赚来的&…

2026/7/24 9:32:08阅读更多 →
AI创业公司GPU租赁决策分析:自建vs租赁成本测算

AI创业公司GPU租赁决策分析:自建vs租赁成本测算

AI创业公司的核心痛点往往不是算法和创意,而是算力成本高、运维压力大、需求波动强。自建GPU集群重资产长周期,很容易消耗初创团队有限的资金和人力。租赁算力则更灵活,但也不是所有场景都划算。本文从成本、运维、弹性三个维度对比自建与租赁…

2026/7/24 9:30:08阅读更多 →
MSP430嵌入式计量库校准参数API详解与高精度电表开发实践

MSP430嵌入式计量库校准参数API详解与高精度电表开发实践

1. 项目概述与计量校准的核心价值 在智能电表、工业能耗监测或者任何需要精确测量交流电参数的嵌入式设备开发中,最让人头疼的往往不是代码逻辑本身,而是如何让硬件测量结果无限逼近真实值。我经历过不止一个项目,硬件板子做出来了&#xff0…

2026/7/24 9:30:08阅读更多 →
企业 Function Calling:日历、邮件和 CRM 的跨系统工具集成

企业 Function Calling:日历、邮件和 CRM 的跨系统工具集成

企业 Function Calling:日历、邮件和 CRM 的跨系统工具集成 一、"帮我约下周三下午和客户的会议"——3 分钟后还在手动操作 一个销售同事在群里说:"帮我约下周三下午 3 点和张总的评审会,顺便发个邮件确认。"这在当前的工…

2026/7/24 15:41:34阅读更多 →
高性能ADC评估平台深度解析:从硬件架构到动态性能测试实战

高性能ADC评估平台深度解析:从硬件架构到动态性能测试实战

1. 项目概述:从芯片到系统,一个高性能ADC评估平台的深度拆解 在精密数据采集系统的设计初期,选型一颗合适的模数转换器(ADC)往往是决定项目成败的关键一步。参数表上的数字固然重要,但如何在实际电路环境中…

2026/7/24 15:41:34阅读更多 →
AI Prompt缓存在智慧养虾中的实践与优化

AI Prompt缓存在智慧养虾中的实践与优化

1. 项目背景与核心价值"OpenClaw人人养虾"这个项目名称乍看有些天马行空,但细究之下其实暗藏玄机。作为一名在农业科技领域摸爬滚打多年的从业者,我第一眼就抓住了两个关键信息点:"养虾"指向水产养殖行业,&qu…

2026/7/24 15:41:34阅读更多 →
树莓派语音点歌台:接上麦克风和音箱就能用的网易云+百度语音方案

树莓派语音点歌台:接上麦克风和音箱就能用的网易云+百度语音方案

本文还有配套的精品资源,点击获取 简介:一套即插即用的树莓派语音点歌系统,用Python开发,直接调用百度智能云语音识别API把说话转成文字,再通过网易云音乐API搜索并播放歌曲。支持语音说歌名、歌手或‘下一首’‘暂…

2026/7/24 15:41:34阅读更多 →
AI生成咖啡店氛围音乐:低成本版权解决方案

AI生成咖啡店氛围音乐:低成本版权解决方案

1. 项目背景与需求解析开咖啡店的朋友们都知道,背景音乐(BGM)对店铺氛围营造至关重要。太吵的音乐会让顾客坐不住,太柔的曲子又容易让人昏昏欲睡。更头疼的是,传统版权音乐要么费用高昂,要么需要频繁更换歌单。最近半年&#xff0…

2026/7/24 15:41:34阅读更多 →
企业级AI应用实战:降本增效与跨部门协作

企业级AI应用实战:降本增效与跨部门协作

1. 活动背景与行业洞察2026年企业级AI应用已经进入深水区,各行业头部企业纷纷开始探索AI技术与实际业务场景的深度融合。建谊集团作为建筑科技领域的创新先锋,此次举办的实战会聚焦"AI共创"核心理念,直指当前企业AI落地过程中的三大…

2026/7/24 15:39:33阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 0:58:53阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 0:58:53阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 0:58:53阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:06阅读更多 →
【LeetCode 54】螺旋矩阵

【LeetCode 54】螺旋矩阵

问题描述: 解法: 1、模拟(参考自【LeetCode 54】螺旋矩阵-CSDN博客) int *spiralOrder(int **matrix, int matrixSize, int *matrixColSize, int *returnSize) {static const int dirs[4][2] {{0, 1}, {1, 0}, {0, -1}, {-1, …

2026/7/24 0:00:06阅读更多 →
2026 WAIC:模型隐身、智能体疯野,厂商竞赛聚焦办公场景与商业闭环

2026 WAIC:模型隐身、智能体疯野,厂商竞赛聚焦办公场景与商业闭环

知春路不相信模型领先今年WAIC大会,昔日AI六小龙来了五家,分别是Kimi、阶跃星辰、Minimax、百川智能、零一万物。连放弃基模的百川和零一万物都来了,唯一缺席的竟是近几个月来风光无限的智谱。(DeepSeek一直不参加)WAI…

2026/7/24 0:00:06阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/23 22:58:43阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/23 18:58:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/23 18:58:18阅读更多 →