我有一个数据模式,其中包含分配给实体的许多更新(每个实体数十万个以上)。我用每个实体的单个顶级文档和其中的更新数组来表示它。这些顶级文档的模式如下:
{
"entity_id": "uuid",
"updates": [
{ "timestamp": Date(...), "value": 10 },
{ "timestamp": Date(...), "value": 11 }
]
}
我正在尝试创建一个查询,返回在过去 n 小时内接收到更新的实体数量。由于我的应用程序更新的方式,updates
数组中的所有更新都保证按顺序排序。我已经创建了以下聚合来完成此操作:
db.getCollection('updates').aggregate([
{"$project": {last_update: {"$arrayElemAt": ["$updates", -1]}}},
{"$replaceRoot": {newRoot: "$last_update"}},
{"$match": {timestamp: {"$gte": new Date(...)}}},
{"$count": "count"}
])
由于某些我不理解的原因,我刚刚粘贴的查询需要耗费很长时间才能完成。它实际上耗尽了我使用的客户端的15秒超时时间。
从时间复杂度的角度来看,这个查询看起来非常便宜(这也是我设计这个模式的一部分原因)。它似乎与集合中顶级文档的总数成线性关系,之后再进行筛选,其中少于10,000个。
令人困惑的是,它似乎不是昂贵的$project步骤。如果我只运行它,查询在不到2秒钟内完成。然而,只要添加$match步骤,它就会超时,并显示数据库服务器上的大量CPU和IO使用情况。我最好的猜测是,由于某种原因,它正在对完整的更新数组执行某些操作,这毫无意义,因为第一步明确限制它只能访问最后一个元素。
有没有办法提高这个聚合操作的性能?是否将所有更新保存在单个数组中会导致Mongo即使数组访问模式本身有效,也无法创建最佳查询?
做之前我之前做过的事情,并将每个更新作为带有其父实体ID标记的顶级文档存储,会更好吗?这是我之前正在做的事情,但性能非常差,我想尝试这个模式,以改进它。到目前为止,经验与我预期/希望的相反。
.0
将查看最旧的更新而不是最新的更新。我可以修改填充更新的应用程序代码,使其插入到数组的前面而不是后面,但这似乎是违反直觉的。此外,我按照此处指定的方式创建了索引,但它似乎对我的原始查询性能没有帮助(考虑到其语义,这并不令人惊讶)。 - Ameo0
而不是-1
...非常抱歉。我已经更正了示例。 - Ameo