Как обрабатывать отношения "многие ко многим" в 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 может справиться с этим очень легко.
- вы можете найти все песни связанные с одним художником в одном запросе, и наоборот.