capped 集合写满后覆盖的顺序是什么?还有哪些限制?这篇用 max=5 和 size=4096 的小集合逐步观察,再验证删除、改大文档、tailable 游标、转换和在线改大小。
test> db.cap5.find({}, {v: 1})
[
{ _id: 4, v: 'doc4' },
{ _id: 5, v: 'doc5' },
{ _id: 6, v: 'doc6' },
{ _id: 7, v: 'doc7' },
{ _id: 8, v: 'doc8' }
]
test> var s = db.cap5.stats(); ({capped: s.capped, max: s.max, maxSize: s.maxSize, count: s.count, size: s.size})
{ capped: true, max: 5, maxSize: 4096, count: 5, size: 130 }capped 集合有 size(字节)与可选的 max(文档数)两个上限,先到先生效,写满后最老的文档被覆盖。
每个文档 194 字节;插入过程中集合文档数依次(每 10 个取一次): [1, 11, 21, 21, 21, 21, 21, 21, 21, 21]
test> var s = db.capsz.stats(); ({count: s.count, size: s.size, maxSize: s.maxSize, storageSize: s.storageSize})
{ count: 21, size: 4074, maxSize: 4096, storageSize: 4096 }
test> db.capsz.find({}, {_id: 1}).toArray().map(d => d._id)
[
79, 80, 81, 82, 83, 84, 85,
86, 87, 88, 89, 90, 91, 92,
93, 94, 95, 96, 97, 98, 99
]size=4096、每个文档 194 字节时,集合稳定保留 21 个最新的文档。
test> db.capsz.deleteOne({_id: 91})
{ acknowledged: true, deletedCount: 1 }
test> db.capsz.updateOne({_id: 92}, {$set: {pad: "y".repeat(400)}})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
test> db.capsz.updateOne({_id: 93}, {$set: {pad: "z".repeat(170)}})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
test> db.capsz.updateOne({_id: 94}, {$set: {pad: "w"}})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
test> db.capsz.replaceOne({_id: 95}, {pad: "y".repeat(400)})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
test> db.capsz.find({_id: {$in: [91, 92, 93, 94, 95]}}).toArray().map(d => [d._id, d.pad.length])
[ [ 92, 400 ], [ 93, 170 ], [ 94, 1 ], [ 95, 400 ] ]在这台 9.0.2 上,删除单个文档、把文档改大、replaceOne 变长都成功了。常见资料里“不能删除、不能增长”的说法至少对这个版本不成立(没有逐个版本核对)。
test> db.capsz.find({}, {_id: 1}).sort({$natural: -1}).limit(3).toArray().map(d => d._id)
[ 99, 98, 97 ]
test> db.capsz.getIndexes().map(i => i.name)
[ '_id_' ]capped 集合的自然顺序就是插入顺序,$natural: -1 可以倒着读最新的;它也带 _id_ 索引。
tailable 游标收到(距启动的秒数, _id): [(0.0, 0), (1.0, 1), (1.7, 2), (2.4, 3)]
对普通(非 capped)集合 tailable 会怎样: error processing query: ns=test.plainTree: $and Sort: {} Proj: {} tailable cursor requested on non capped collection, full error: {'ok': 0.0tailable 游标可以像 tail -f 一样等待新文档,只能用在 capped 集合上,普通集合会报错。
转换前 reg: 1000 个文档,capped=False
convertToCapped(size=32768): {'ok': 1.0}
转换后: capped=True,count=264,maxSize=32768,size=32736(最老的文档被丢弃,留下最新的)
最早的 _id = 736,最晚的 _id = 999
collMod cappedSize=16384, cappedMax=50: {'ok': 1.0}
之后: count=264, maxSize=16384, max=50
再插入 1 个文档之后: count=50(新的上限立刻生效),最早的 _id = 951convertToCapped 把普通集合转成 capped,保留最新的文档;collMod 可以在线改 cappedSize、cappedMax,下一次插入时才裁剪到新上限。