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可选。
若同时指定max和size,先达到任一限制即触发覆盖。
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: 10MB,max: 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().capped → true |
| 能否增大 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()
参考资料
- 官方文档:Capped Collections
- Tailable Cursor:MongoDB Manual
db.createCollection:API 参考
一句话总结
固定集合 = 高性能日志环 + 天然顺序 + 零维护覆盖
适用于 写多读少、顺序消费、容量可控 的所有场景。
需要:
- 完整 Node.js / Python 实时消费示例?
- 与 RockMongo 查看固定集合?
- 迁移普通集合到 capped 的脚本?
欢迎继续提问!