mongodb日志文档是普通集合文档,需精简字段名、统一数值类型、结构化裁剪、敏感脱敏、长文本外置、ttl索引自动过期、稀疏索引合理使用,并配合compact释放空间。

MongoDB 日志文档本身不是数据库内置的“日志类型”,而是指你用 db.collection.insertOne() 存进去的、用于记录行为(如操作审计、错误追踪、用户行为)的普通文档。这类文档最容易因无约束地疯长而吃光磁盘——不是因为单条大,而是字段冗余、结构松散、冷数据不清理。
字段命名越短越好,尤其在高频写入场景
WiredTiger 对每个文档都存储字段名字符串。100 万条日志,timestamp 字段名重复存 100 万次,比用 t 多占约 8MB;user_id 改成 uid、error_message 缩成 emsg,积少成多很可观。
- 避免驼峰或下划线长名:
requestUrl→ru,responseStatusCode→sc - 数值类字段统一用整型:
"sc": "200"(string)→"sc": 200(int),减少类型标记开销 - 布尔值别存
"true"/"false"字符串,直接用true/false布尔类型
别把日志当原始 dump,提前做结构化裁剪
开发常图省事把整个 req.body 或 err.stack 直接塞进日志文档,结果一条错误日志动辄几 MB。WiredTiger 压缩对长重复文本有效,但对随机堆栈、base64 图片、未压缩 JSON 效果极差。
- 只存关键上下文:
err.name、err.code、err.message,丢弃完整stack - 敏感字段必须脱敏:
password、token、id_card等字段写入前清空或哈希,否则既占空间又违规 - 长文本字段(如
raw_payload)单独抽离,用payload_id引用,或改存到对象存储(S3/OSS)+ URL 字段
用 TTL 索引自动过期冷日志,别靠人工删
db.logs.createIndex({ "t": 1 }, { expireAfterSeconds: 2592000 })(30 天)是唯一可持续的方案。手动 db.logs.deleteMany() 在大数据量下会锁表、拖慢写入,且容易漏删或误删。
- TTL 索引字段必须是 Date 类型,不能是字符串时间戳;确保插入时用
new Date()而非"2026-07-21" - 后台删除任务每 60 秒扫描一次,延迟可接受,但别设
expireAfterSeconds: 60这种激进值——可能刚写入就被删 - 若需保留部分日志更久(如 error 级别),建两个集合:
logs_info(TTL 7 天)、logs_error(TTL 90 天)
稀疏索引只对真正缺失率高的字段有用
比如日志中 error_code 仅在出错时存在,缺失率 >60%,这时 db.logs.createIndex({ error_code: 1 }, { sparse: true }) 才省空间。但若你给 t(时间戳)加 sparse: true,不仅无效,还会让所有查询无法命中索引。
- 先估算缺失率:
db.logs.aggregate([ { $group: { _id: { $eq: [ { $type: "$error_code" }, "missing" ] } , count: { $sum: 1 } } } ]) - 别在复合索引里混用稀疏字段:
{ t: 1, error_code: 1 }设为稀疏,意味着只要任一字段缺失,整条索引项就不存在 - 如果要查
{ error_code: { $exists: false } },稀疏索引反而让查询变慢——它根本不会覆盖“缺失”状态
真正卡住空间释放的,往往不是单条文档大小,而是你没意识到:WiredTiger 的缓存和磁盘文件是两套体系,文档删了,.wt 文件体积未必立刻缩小;得靠 compact 或后台碎片整理,而这需要足够空闲 I/O 和内存余量。所以模型精简 + TTL 自动清理,才是日常可控的起点。











