直接用普通集合存传感器数据会导致写入压力大、磁盘占用高、聚合查询慢,因1000台设备每秒上报1次,一天达8640万文档,引发索引膨胀、wal日志暴涨、$group扫描成本激增。

为什么不能直接用普通集合存传感器数据
每条温度/湿度记录单独存一个文档,写入压力大、磁盘占用高、聚合查询慢。比如 1000 台设备每秒上报 1 次,一天就是 8640 万文档——索引膨胀、WAL 日志暴涨、$group 扫描成本直线上升。这不是 MongoDB 的瓶颈,是模型没对齐访问模式。
Bucket Pattern 的核心:应用层按时间+设备维度聚合写入
它不依赖 MongoDB 特性,而是由应用控制“把多少条原始点塞进一个桶文档”。典型结构长这样:
{
"_id": "sensor-001_2026-09-07T08:00:00Z",
"device": "sensor-001",
"start": ISODate("2026-09-07T08:00:00Z"),
"end": ISODate("2026-09-07T09:00:00Z"),
"samples": [
{ "ts": ISODate("2026-09-07T08:00:12Z"), "temp": 23.4, "hum": 65 },
{ "ts": ISODate("2026-09-07T08:00:45Z"), "temp": 23.6, "hum": 64 }
],
"stats": {
"count": 240,
"avgTemp": 23.52,
"maxHum": 72
}
}
关键点:
- 桶粒度按业务定:
minutes(温湿度)、hours(电表读数)、days(日志归档) -
samples数组长度要可控,避免突破 16MB 文档上限;IoT 场景建议单桶 ≤ 1000 条原始点 - 必须建复合索引:
{ "device": 1, "start": -1 },否则按设备查最近 N 小时桶会全表扫描 - 写入时用
upsert+$push累加样本,避免读-改-写竞态
和原生 Time Series 集合比,Bucket Pattern 适合什么场景
原生 timeseries 集合压缩率高、语法简洁,但限制死硬;Bucket Pattern 是手动可控的折中方案,适合:
- 需要在桶内保留原始精度(比如做 FFT 分析),而
granularity: "minutes"会让秒级波动被抹平 - 元数据动态变化频繁(如设备固件升级后新增字段),
metaField不支持 schema 变更 - 要跨设备做关联计算(如“同一厂房内所有传感器温差>5℃的桶”),原生 TS 集合无法
$lookup关联其他集合 - 已有系统迁移成本低——不用改写入逻辑,只需把单点插入改成桶文档
updateOne
注意:samples 数组里的时间戳别用字符串存,必须是 Date 类型,否则 $dateTrunc 或范围查询会失效。
容易被忽略的坑:桶边界对齐和 TTL 清理
桶的 start/end 必须严格对齐(如整点、整分),否则跨桶查询会漏数据。例如按小时分桶,却用 new Date().toISOString().slice(0,13) 截取,遇到夏令时切换可能错位。
TTL 索引不能只建在 start 上——得用 { "end": 1 },因为桶文档的生命周期由结束时间决定。错误示例:db.buckets.createIndex({ "start": 1 }, { expireAfterSeconds: 86400 }) 会导致刚写入的桶立刻被删。
最后提醒:Bucket Pattern 是权衡,不是银弹。如果业务只要求“查某设备过去 24 小时平均值”,直接上原生 timeseries 集合更省心;只有当原始点价值高、聚合逻辑复杂、或需强一致性关联时,才值得手动维护桶结构。











