MongoDB模式设计:何时使用扩展引用模式与依赖聚合管道



我希望更普遍地理解,当扩展的参考模式应该依赖于MongoDB的聚合管道使用,但据我所知,答案往往是"这取决于你的用例"。对于MongoDB模式设计,我将在以下场景中解决这个问题:

  • 我们正在模拟实验室测试数据,其中对样品进行测试,并且样品属于"集合";的样本。
  • 这是一对多的关系:许多样本属于一个集合。一组样本的上界约为~20。
  • 在60-70%的时间里,集合和样本是一起被访问和更新的,但是在另外30-40%的时间里,样本也会被过滤/排序/分页/更新。
  • 设备和样品都被频繁操作。
  • 一些关于集合的查询必须确定是否,以及每个集合中有多少个样本具有特定的状态。

基于此场景,由于最后提到的查询,香草引用是不可行的。扩展引用模式在这里看起来很自然,因为集合可以包含一个示例引用数组以及它们的状态。然而,由于集合和样本是如此频繁地一起被访问,因此将样本作为嵌套的子文档嵌入到集合中似乎也是很自然的,因为在访问集合时需要集合的每个样本的完整。

我一直在研究使用聚合管道对嵌套子文档进行查询/操作,即在集合文档中嵌套样本,并且API似乎足够健壮,可以实现所需的所有查询和操作。然而,直觉上,我怀疑如此频繁地查询/操作嵌套子文档相对于它们自己的集合中的样本来说是低效的。我也一直在阅读聚合管道限制,管道中的每个步骤都有100MB的限制。如果有足够聪明的索引和预先的$match操作来缩小数据范围,我认为不应该达到这个限制,但似乎确实存在相当明显的可伸缩性权衡。

作为参考,下面是我想到的一个聚合查询示例:

[
// Get all sets that have samples with a status of "Broken"
{ $match: { "samples.status": "Broken" } },
// Filter out samples in the sets that don't have a status of "Broken"
{
$project: {
samples: {
$filter: {
input: "$samples",
as: "sample",
cond: { $eq: ["$$sample.status", "Broken"] },
},
},
},
},
// Create a new document in memory for each sample
{ $unwind: "$samples" },
// Sort the samples based on break date
{ $sort: { "samples.breakDate": -1 } },
// Operations relevant to pagination
{
$facet: {
count: [{ $count: "count" }],
entities: [{ $skip: 1000 }, { $limit: 50 }, { $replaceRoot: { newRoot: "$samples" } }],
},
},
// Improving the shape of the output
{
$project: {
count: { $arrayElemAt: ["$count.count", 0] },
entities: "$entities",
},
},
]

最激进的方法是在他们自己的集合中复制样本,并在集合中作为嵌套的子文档。示例文档非常小(约15个字段),因此这将使空间折衷稍微更容易接受,但是数据一致性就变成了一个问题。集合和示例经常更新,因此每个示例更新将需要多个操作,并使用原子事务。

似乎没有明显的赢家。除了对每种方法的性能进行一些实际测试外,在决定是使用扩展引用模式还是使用聚合管道对嵌套子文档进行查询/操作时,应该考虑哪些一般准则?

100MB限制仅适用于RAM。如果您允许使用磁盘,它将工作,但性能会下降。

你触及了最重要的问题,所以这真的是关于现实世界的测试。使用mgodatagen或类似的工具生成样本数据集,并测试您的典型查询、使用索引并比较性能。确保您的测试数据库按比例缩小到测试数据集。

如果您正在使用mongoose,请考虑它在事务之上添加的文档版本控制。如果"集和样本频繁更新",则需要在应用程序级别处理与陈旧数据相关的错误。频繁到足以引起并发更新。

除了部分数据复制之外,还有预聚合,这对于诸如"每个集合中有多少样本具有特定状态"之类的查询来说非常神奇。

基本上,在mongodb模式设计的情况下,它取决于你的用例。真正的含义是"你有多少数据","你运行了什么查询","你插入/查询/更新文档的频率","你可以在集群上花多少钱";等等结合起来。实际上是在做真实世界的测试。

最新更新