如何处理MongoDB中的多对多关系?

Posted

技术标签:

【中文标题】如何处理MongoDB中的多对多关系?【英文标题】:How to handle Many to Many relationship in mongoDB? 【发布时间】:2018-09-17 13:24:31 【问题描述】:

我对 MongoDB 中的多对多关系实现有一个特定的问题。

我收藏了歌曲艺术家(百万文档)。在这里可以唱这首歌 by Many Artists 一个艺术家可以唱很多歌。所以我跟着 两个集合中的文档引用方法。像这样……

1. 歌曲合集:-


  _id:ObjectId("dge547567hheheasfw3454dfg"),
   title:"xyz",
   artists:[ObjectId("xfvdg464654"), ...] //many artists // artists ids

2. 艺术家收藏:-


  _id:ObjectId("dge547567hheheasfw3454dfg"),
   title:"xyz",
   songs:[ObjectId("xfvdg464654"), ...] //many songs // songs Ids 

但这里的问题是,在删除艺术家时,我必须从包含艺术家的歌曲的所有文档中的艺术家数组中删除艺术家,反之亦然。这会导致 原子性问题。 这里如何保证原子性?

其次,当数据库增长并由艺术家演唱歌曲时 将增加因此导致文档集的增长和文档大小可以达到 16MB 或更大(MAX DOC SIZE)。

那么在这种情况下可以做些什么呢?

【问题讨论】:

But here the problem is while doing CRUD Operation on one collection I have to do CRUD operation on other collection. which can cause the problem of Atomicity -> MongoDB 将这项工作留给必须明确确保数据一致性的程序员。 MongoDB 确保 document 级别的原子性。 Secondly when the database will grow and songs are sung by the artist will increase thus resulting document growth of both collection and document size can reach to 16MB or greater -> 你没有存储整个歌曲/艺术​​家收藏,只是一个 ID,所以理想情况下这里没有问题.. 【参考方案1】:

让我们从针对您的案例详细说明我们的多对多关系开始,并尝试了解可以做什么和不可以做什么 -

一首歌可以由多达 10 位或 20 位艺术家演唱(假设它并不复杂/多样化,可能需要 100 位艺术家)。

在这种情况下,在 songs 集合中存储艺术家的 id 非常好,我们可以放心地假设即使在最坏的情况下 (存储由 100 位艺术家演唱的复杂/多样化的歌曲)它永远不会强制我们的歌曲集超过 16 MB。

然而,一个艺术家在他的整个职业生涯中可能会唱多达 1000 首或更多的歌曲。 ObjectId 的长度为 12 字节,在这种情况下,集合将增长到仅 12000 字节的大小,远小于 16000000 字节。你仍然有很多空间。因此无需担心达到 16MB 的上限。

方法 - 1

Inter-bucketing 对于期望高读取的关系非常有效。

可以在单个查询中获取某些艺术家的歌曲,反之亦然。如果在这两个集合上散布索引,这将更加顺畅。

但是,如果我们在歌曲和歌曲中将艺术家分桶,那么我们的更新就不再是原子的了,但为此我们仍然可以为艺术家和歌曲 CRUD 实现应用程序级别的两阶段提交,即使在有点麻烦,解决问题。

方法 - 2:

为什么不只在歌曲收藏中存储艺术家 ID 并拥有 该字段的多键索引。

演唱歌曲的艺术家列表比艺术家演唱的歌曲列表太短。所以我们只在歌曲收藏中存储艺术家。

这样我们会 -

1.如果我们将歌曲分桶到艺术家收藏中,则可以避免达到艺术家收藏最大尺寸的几乎不可能的可能性。

2. 避免为至少 songs 集合编写 2P 提交。所有关系读取都只能通过歌曲集来满足(这里我不包括艺术家的 _id 查找)

3.确保在单个查询中快速访问数据,即使在反向查询艺术家演唱的歌曲的歌曲集时也是如此。

您已经拥有一些需要为其获取歌曲的艺术家的信息(_id)。您只需像这样草拟一个查询 -

 db.songs.find( artists: 'your-artist-id' );

当您解释这个查询时,当您意识到它利用了您的多键索引时,您会感到很高兴。干得好!

现在选择哪种方法?

我发现第二种方法更适合您的用例,因为它降低了管理 2P 提交以实现原子性的一些复杂性,并且仍然提供了良好的读取性能。第一种方法肯定是面向读取的,所以如果你确定你会在这两个集合上收到大量的读取,请选择第一个,否则第二个应该可以解决问题。

【讨论】:

【参考方案2】:

我通过采用类似于我们在 sql 中所做的第三个集合在 mongodb 中实现了多对多关系。

歌曲合集


  _id:ObjectId("dge547567hheheasfw3454df12"),
   title:"xyz",
   length : 123

艺术家收藏


   _id:ObjectId("dge547567hheheasfw3454d32"),
   name:"abc",

SongArtist 合集


   _id:ObjectId("dge547567hheheasdfsdfsdfgdfga42"),
   artist: ObjectId("dge547567hheheasfw3454dfg32"),
   song: ObjectId("dge547567hheheasfw3454df12"),

现在,当您进行 crud 操作时,如果您想从歌曲中删除艺术家 您可以在 SongArtist Collection 的单个查询中完成。 超过文档大小永远不会有任何问题 如果您想删除您拥有的特定歌曲中的特定艺术家 查询一次 它会增加集合中的记录数,但 mongodb 可以轻松处理。 您可以在单个查询中找到与一位艺术家相关的所有歌曲,反之亦然。

【讨论】:

@abhishek 请告诉您是否对我的解决方案有任何其他疑问

以上是关于如何处理MongoDB中的多对多关系?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理 JSON 中的多对多关系?

如何处理淘汰视图模型中的多对多关系

MongoDB 多对多关联

使用 Spring Boot、Jackson 和 Hibernate 的多对多关系

了解 MongoDB 中的多对多关系以及如何取消引用集合

MongoDB中的多对多关系