MongoDB 固定集合(Capped Collections)

MongoDB 固定集合(Capped Collections)完整指南

固定集合(Capped Collection) 是 MongoDB 中一种 预分配固定大小、自动覆盖旧文档 的特殊集合,类似 环形缓冲区(Circular Buffer),非常适合 日志、实时数据流、缓存、消息队列 等场景。


1. 核心特性

特性说明
固定大小创建时指定 size(字节),不可更改
自动覆盖插入新文档时,若空间不足,自动删除最旧的文档(FIFO)
插入顺序保留文档按插入顺序存储,天然有序
不支持删除/更新只能 插入整体删除不能删除单条或修改文档
高性能预分配空间 + 无索引移动 → 写入极快
天然索引自动为 _id 创建索引,且 顺序递增

2. 创建固定集合

2.1 使用 createCollection(推荐)

db.createCollection("logs", {
   capped: true,
   size: 1048576,        // 1 MB(字节)
   max: 1000             // 最多文档数(可选)
})

size 必须指定,max 可选。
若同时指定 maxsize先达到任一限制即触发覆盖

2.2 转换普通集合为固定集合(MongoDB 4.0+)

db.runCommand({
   convertToCapped: "mycoll",
   size: 1048576
})

注意:会复制所有数据到新集合,原集合被删除。


3. 使用示例

示例:实时日志系统

// 1. 创建 10MB 的日志集合
db.createCollection("app_logs", {
   capped: true,
   size: 10 * 1024 * 1024,
   max: 5000
})

// 2. 插入日志
db.app_logs.insertOne({
   level: "INFO",
   message: "User login success",
   user: "alice",
   ts: new Date()
})

// 3. 查询最新 10 条(利用天然顺序)
db.app_logs.find().sort({ $natural: -1 }).limit(10)

// 4. Tailable Cursor(持续监听新日志)
const cursor = db.app_logs.find().addOption(DBQuery.Option.tailable)
                          .addOption(DBQuery.Option.awaitData)

while (cursor.hasNext()) {
   printjson(cursor.next())
}

4. 高级用法:Tailable Cursor(尾游标)

类似 tail -f,用于 实时消费 固定集合的新数据。

// Node.js 示例(mongodb 驱动)
const cursor = db.collection('events')
  .find()
  .sort({ $natural: 1 })
  .addCursorFlag('tailable', true)
  .addCursorFlag('awaitData', true)

cursor.on('data', doc => console.log('New event:', doc))

适用场景

  • 实时监控
  • 消息队列(配合 max 控制队列长度)
  • 事件溯源(Event Sourcing)

5. 关键限制与注意事项

限制说明
不可更改大小size 一旦设定,无法 collMod 增大或缩小
不能删除单文档只能 drop() 整个集合
不能更新文档任何 update 都会失败(CannotChangeDocumentInCappedCollection
不支持分片固定集合 不能作为分片集合(sharded collection)
不支持 TTL 索引不能设置 expireAfterSeconds
复制延迟风险主从复制中,Secondary 可能落后,需用 awaitData

6. 性能对比(普通集合 vs 固定集合)

操作普通集合固定集合
插入O(log n)O(1)
查询(按 _id)O(log n)O(log n)
查询(最新 N 条)需要索引天然顺序$natural 极快
空间管理动态增长预分配,无碎片
内存占用按需预分配全部 size

写入吞吐可提升 3~10 倍(尤其高并发日志场景)


7. 最佳实践

场景推荐配置
应用日志size: 100MB ~ 1GB,不设 max
实时指标size: 10MBmax: 10000
消息队列size + max,配合消费者 ack
IoT 数据流tailable cursor + awaitData

推荐索引(仅需一个)

// 天然顺序查询最新数据
db.logs.find().sort({ $natural: -1 }).limit(100)

// 若需按字段查询,加稀疏索引(谨慎!会影响插入性能)
db.logs.createIndex({ level: 1 }, { sparse: true })

8. 常见问题(FAQ)

问题解答
如何清空固定集合?db.logs.drop() → 重新 createCollection
如何查看是否为固定集合?db.logs.stats().cappedtrue
能否增大 size?不能。需:导出 → drop → 重新创建 → 导入
支持事务吗?支持(MongoDB 4.0+),但因不能更新,实际意义不大
如何备份?mongodump 正常备份,恢复后仍是 capped

9. 替代方案对比

方案优点缺点
Capped Collection高性能、天然顺序、简单不可改、不可删
TTL 索引自动过期需索引,性能略低
Kafka / Redis Stream分布式、高可用需额外系统
Change Streams实时、全量资源消耗大

推荐高性能写、顺序读、简单运维 → 选 Capped Collection


10. 一键脚本:创建 + 监控

#!/bin/bash
# create_capped.sh

DB="mydb"
COLL="sensor_data"
SIZE=$((50 * 1024 * 1024))  # 50 MB

mongosh --quiet <<EOF
use $DB
db.createCollection("$COLL", {
  capped: true,
  size: $SIZE,
  max: 100000
})
print("固定集合 $COLL 创建成功,大小: ${SIZE} bytes")
EOF
// monitor.js(实时打印最新数据)
const { MongoClient } = require('mongodb')
async function tail() {
  const client = await MongoClient.connect('mongodb://localhost:27017')
  const coll = client.db('mydb').collection('sensor_data')

  const cursor = coll.find()
    .sort({ $natural: 1 })
    .addCursorFlag('tailable', true)
    .addCursorFlag('awaitData', true)

  for await (const doc of cursor) {
    console.log(new Date(), doc)
  }
}
tail()

参考资料


一句话总结

固定集合 = 高性能日志环 + 天然顺序 + 零维护覆盖
适用于 写多读少、顺序消费、容量可控 的所有场景。


需要

  • 完整 Node.js / Python 实时消费示例?
  • 与 RockMongo 查看固定集合?
  • 迁移普通集合到 capped 的脚本?

欢迎继续提问!

文章已创建 2481

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部