Skip to content

MongoDB WiredTiger 存储引擎原理

MongoDB 为什么能在高并发场景下表现出色?答案之一就是 WiredTiger 存储引擎

从 MongoDB 3.0 开始,WiredTiger 成为默认存储引擎(MMAPv1 已废弃)。这一篇,我们来深入了解它的原理。

WiredTiger 概述

WiredTiger 是一款高性能、支持并发、压缩存储的存储引擎:

特性说明
B+Tree默认索引结构
LSM Tree可选的日志结构存储引擎
MVCC多版本并发控制
文档级锁高并发写入
压缩支持 snappy、zstd、zlib
Checkpoint定期刷盘保证一致性

数据存储结构

B+Tree 结构

┌─────────────────────────────────────────────────────────────┐
│                       B+Tree                                  │
│                                                              │
│                         [Root]                               │
│                        /     \                               │
│                    [Page]   [Page]                          │
│                   / | \    / | \                            │
│                 ...  ...  ...  ...                         │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    Data Page                           │   │
│  │  ┌──────────┬──────────┬──────────┬──────────┐     │   │
│  │  │ Record 1 │ Record 2 │ Record 3 │ Record 4 │     │   │
│  │  └──────────┴──────────┴──────────┴──────────┘     │   │
│  │  ~16KB per page (默认)                               │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

Page 类型

类型说明大小
leaf page存储文档数据约 16 KB
internal pageB+Tree 索引页约 4 KB
overflow page大字段溢出页按需
empty page已删除页面回收重用

WiredTiger vs MMAPv1

特性WiredTigerMMAPv1(已废弃)
并发控制文档级锁集合级锁
内存管理WiredTiger Cache操作系统 Page Cache
压缩支持不支持
索引B+TreeB-Tree
空间回收自动需 compact

Write-Ahead Logging (WAL)

日志机制

WiredTiger 使用 Write-Ahead Logging 保证数据持久性:

写入操作


┌─────────────────┐
│  Journal Log    │  ← 先写入日志(持久化)
│  (WAL 文件)     │
└─────────────────┘


┌─────────────────┐
│  Data File      │  ← 异步写入数据文件
│  (Checkpoint)   │
└─────────────────┘

Journal 配置

javascript
// 查看 Journal 状态
db.adminCommand({serverStatus: 1}).durability

// Journal 刷新间隔(毫秒)
// 默认每 100ms 刷新一次

Journal 与崩溃恢复

javascript
// Journal 文件存储在 --dbpath/journal 目录
// WiredTiger WAL: WiredTigerLog.*.7f...

// 崩溃恢复过程:
// 1. MongoDB 启动
// 2. 读取 Journal
// 3. 重放未刷盘的修改
// 4. 恢复到一致状态

Cache 与内存管理

WiredTiger Cache

┌──────────────────────────────────────────────────────────────┐
│                     MongoDB 进程内存                          │
│                                                               │
│  ┌────────────────────────────────────────────────────────┐ │
│  │              WiredTiger Cache (默认 50% RAM)            │ │
│  │  ┌────────────────────────────────────────────────────┐ │ │
│  │  │  Data Pages (热点数据)                             │ │ │
│  │  │  ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐    │ │ │
│  │  │  │ Page 1 │ │ Page 2 │ │ Page 3 │ │ Page 4 │    │ │ │
│  │  │  └────────┘ └────────┘ └────────┘ └────────┘    │ │ │
│  │  └────────────────────────────────────────────────────┘ │ │
│  └────────────────────────────────────────────────────────┘ │
│                                                               │
│  ┌────────────────────────────────────────────────────────┐ │
│  │            MongoDB 内部结构 (约 1-2 GB)                │ │
│  └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘

Cache 配置

javascript
// 查看 Cache 使用情况
db.serverStatus().wiredTiger.cache

// 输出示例
{
  "tracked dirty pages in the cache": 123,
  "pages currently held in the cache": 4567,
  "total page size in cache": "789MB",
  "maximum bytes configured": "10GB",
  "percentage of maximum bytes used": "7.8%"
}

内存配置建议

javascript
// 配置 WiredTiger Cache 大小(命令行启动)
mongod --wiredTigerCacheSizeGB=8

// 或在配置文件
storage:
  wiredTiger:
    engineConfig:
      cacheSizeGB: 8

经验法则:WiredTiger Cache 设为机器内存的 50-60%,其他给操作系统。

并发控制:MVCC

多版本并发控制

WiredTiger 使用 MVCC 实现并发读写:

javascript
// 读操作:获取快照,读取一致的数据
// 写操作:创建新版本,不阻塞读操作

// 示例
Tx1: 读取文档 v1
Tx2: 更新文档 v1 → v2(不阻塞 Tx1)
Tx1: 继续读取 v1(不受 Tx2 影响)
Tx1: 提交
Tx2: 提交

文档级锁

javascript
// WiredTiger 支持文档级锁
// 不同文档的写入可以并发

// 锁粒度对比:
// MMAPv1: 集合级锁
// WiredTiger: 文档级锁

// 批量写入优化
// 无序写入可以并发处理
db.orders.insertMany(docs, {ordered: false})

索引结构

B+Tree 索引

javascript
// WiredTiger 默认使用 B+Tree 索引
db.users.createIndex({email: 1})

// B+Tree 特点:
// - 所有数据在叶子节点
// - 叶子节点链表连接
// - 范围查询高效

索引与数据文件的关系

┌──────────────────────────────────────────────────────────────┐
│                       索引文件                                 │
│  ┌────────────────────────────────────────────────────────┐ │
│  │ email: "a@test.com"  →  Point to Page 5               │ │
│  │ email: "b@test.com"  →  Point to Page 3              │ │
│  │ email: "c@test.com"  →  Point to Page 7              │ │
│  └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘

                           │ 指向

┌──────────────────────────────────────────────────────────────┐
│                       数据文件                                 │
│  ┌────────────────────────────────────────────────────────┐ │
│  │ Page 3: {_id: "b", email: "b@test.com", ...}         │ │
│  │ Page 5: {_id: "a", email: "a@test.com", ...}         │ │
│  │ Page 7: {_id: "c", email: "c@test.com", ...}         │ │
│  └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘

Checkpoint(检查点)

Checkpoint 机制

Checkpoint 是数据一致性的保证:

javascript
// 默认每 60 秒创建一个检查点
// 检查点将所有脏页刷盘

// Checkpoint 过程:
// 1. 获取所有脏页列表
// 2. 刷盘所有脏页
// 3. 记录检查点位置(Journal)
// 4. 允许从该位置恢复

// 查看检查点信息
db.serverStatus().wiredTiger.checkpoint

Checkpoint 配置

javascript
// 配置文件
storage:
  wiredTiger:
    engineConfig:
      journalCompressor: snappy
    checkpoint:
      (1) wait: 60        # 等待时间(秒)
      (2) scopy: true      # 快照时复制

压缩

压缩算法

算法压缩率CPU 开销适用场景
snappy中等默认,推荐
zlib存储敏感
zstdMongoDB 4.2+
none高性能需求

压缩配置

javascript
// 集合级别压缩
db.createCollection("logs", {
  storageEngine: {
    wiredTiger: {
      configString: "block_compressor=snappy"
    }
  }
})

// 索引压缩
db.users.createIndex(
  {email: 1},
  {storageEngine: {wiredTiger: {configString: "block_compressor=zlib"}}}
)

// 全局配置
storage:
  wiredTiger:
    engineConfig:
      directoryForIndexes: false
    collectionConfig:
      blockCompressor: snappy
    indexConfig:
      prefixCompression: true

空间回收

WiredTiger 自动回收

javascript
// WiredTiger 使用增量回收
// 不需要手动 compact

// 查看存储统计
db.stats()

// 输出
{
  "dataSize": "10GB",      // 数据大小(压缩前)
  "storageSize": "5GB",     // 磁盘占用(压缩后)
  "indexSize": "2GB",       // 索引大小
  "fileSize": "12GB"        // 文件总大小
}

manual 回收(可选)

javascript
// compact 命令可以回收空间
db.runCommand({compact: "collectionName"})

// 注意:
// 1. 需要额外磁盘空间
// 2. 会在副本集节点上执行
// 3. 会阻塞操作(建议在从节点执行)

Java 监控 WiredTiger

java
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.bson.Document;

public class WiredTigerMonitor {
    public static void main(String[] args) {
        try (MongoClient client = MongoClients.create()) {
            // 获取 WiredTiger 缓存统计
            Document cache = client.getDatabase("admin")
                .runCommand(new Document("serverStatus", 1))
                .get("wiredTiger", Document.class)
                .get("cache", Document.class);

            long maxCacheSize = cache.getLong("maximum bytes configured");
            long usedCacheSize = cache.getLong("size in memory");

            System.out.println("Cache 最大: " + (maxCacheSize / 1024 / 1024 / 1024) + " GB");
            System.out.println("Cache 使用: " + (usedCacheSize / 1024 / 1024 / 1024) + " GB");

            // 获取事务统计
            Document txn = cache.get("transaction", Document.class);
            System.out.println("当前活跃事务: " + txn.get("transaction_thread count"));
        }
    }
}

总结

WiredTiger 核心特性:

特性说明
B+Tree默认索引结构
MVCC多版本并发控制,文档级锁
WAL先写日志再写数据,保证持久性
Cache默认 50% RAM,减少磁盘 I/O
压缩snappy/zlib/zstd,减少存储
Checkpoint定期刷盘,保证一致性

关键配置参数

参数默认值说明
cacheSizeGB50% RAMWiredTiger Cache 大小
journalCompressorsnappyJournal 压缩算法
blockCompressorsnappy数据压缩算法
checkpoint.wait60 秒检查点间隔

下一步,你可以:

基于 VitePress 构建