Как обрабатывать отношения "многие ко многим" в mongoDB?

у меня есть конкретная проблема со многими-многими реализациями отношений в MongoDB.

у меня есть коллекции песни и художники(миллионы документов). Здесь песню можно спеть многие артисты и артист могут спеть много песен. Поэтому я последовал за подход к использованию ссылок на документы в обеих коллекциях. вроде этого...

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 
}

но здесь проблема в том, что при выполнении операции CRUD в одной коллекции я должен выполнять операцию CRUD в другой коллекции.Как и удаление исполнителя, я должен удалить исполнителя из массива исполнителя во всех документах песни, в которой есть исполнитель. что может вызвать проблема атомарности. как я могу обеспечить атомарность здесь?

во-вторых, когда база данных будет расти и песни поют художника увеличит таким образом приводя к рост документа и собрания и размер документа может достигнуть до 16MB или большой(максимальный размер DOC).

Итак, что можно сделать здесь в этом случае?

2 ответов


давайте начнем с подробного описания наших отношений "многие ко многим" для вашего случая и попытаемся понять, что можно и нельзя сделать -

  • песня может быть спета целых 10 или, может быть, 20 художников (предполагая, что это не так сложно/разнообразно, чтобы потребовать, возможно, 100-х художников).

    в этом случае, bucketing id художника внутри songs коллекция отлично подходит, и мы можем с уверенностью предположить, что даже в худшем случае(хранение сложные/разнообразные песни 100 художник) это никогда не заставит нашу коллекцию песен за 16 МБС.

  • художник, однако, может очень хорошо петь до 1000 песен или, может быть, больше в своей карьере enitre. ObjectId длиной 12 байт в этом случае будет увеличивать коллекцию до размера всего 12000 байт, что меньше 16000000 байт. У вас все еще остается много места. Так что не нужно беспокоиться о удара крышка 16Мб.

подход - 1

Inter-bucketing работает очень хорошо для отношений, ожидающих высоких чтений.

песни для некоторых исполнителей можно получить в одном запросе и даже наоборот. И это было бы еще более гладко с индексами, разбросанными по этим двум коллекциям.

но если мы идем вокруг bucketing художников внутри песен и песен внутри художников, то наши обновления не более атомарные, но для что мы все еще можем реализовать двухфазную фиксацию уровня приложения как для исполнителей, так и для песен CRUD, что даже после того, как это немного хлопотно, решает проблему.

подход - 2:

почему не ведро только исполнитель id внутри коллекции песен и имеют индекс multikey в этом поле.

список исполнителей, которые пели песню, слишком короткий, чем список песен, спетых художником. Так что мы только ведро художников внутри коллекция песен.

мы -

1. избегайте почти невозможной возможности поразить максимальный размер коллекции художника, если бы у нас были bucketed песни внутри коллекции художников.

2. избегайте написания коммитов 2P по крайней мере songs сборники. Все реляционные чтения могут быть удовлетворены только через коллекцию песен (здесь я исключаю поиск _id для исполнителя)

3. обеспечьте быстрые данные доступ только в одном запросе, даже при обратном запросе на коллекцию песен для песен, спетых исполнителем.

У вас уже будет некоторая информация (_id) исполнителя, для которой вам нужно получить песни. Вы просто набросаете такой запрос -

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

и когда вы объясняете этот запрос, вы находите счастье, когда понимаете, что он использует ваши многоключевые индексы. Отличная работа !

Теперь, какой подход пойти ?

Я нахожу второй подход a немного более тонкий для вашего случая использования, поскольку он уменьшает некоторую сложность управления коммит 2P для атомарности и по-прежнему обеспечивает хорошую производительность чтения. Первый подход определенно ориентирован на чтение, поэтому, если вы уверены, что будете получать много и много чтений в обеих коллекциях, перейдите к первому, иначе второй должен сделать трюк.


я реализовал многие ко многим отношениям в mongodb, взяв третью коллекцию, похожую на то, что мы делаем в sql.

Коллекция Песен

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

Коллекция Художника

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

Коллекция SongArtist

{
   _id:ObjectId("dge547567hheheasdfsdfsdfgdfga42"),
   artist: ObjectId("dge547567hheheasfw3454dfg32"),
   song: ObjectId("dge547567hheheasfw3454df12"),
}
  • теперь, когда вы делаете операции crud и если вы хотите удалить исполнителя из песни вы можете сделать это в одном запросе в коллекции SongArtist.
  • он будет никогда не возникнет проблем с превышением размера документа
  • если вы хотите удалить конкретного исполнителя в конкретной песне у вас есть запросить один раз
  • это увеличит количество записей в коллекции, но mongodb может справиться с этим очень легко.
  • вы можете найти все песни связанные с одним художником в одном запросе, и наоборот.