Несколько вопросов о mongodb?

Здравствуйте, никогда не работал с mongodb, начал разбираться и не совсем понятно вот что: в разных статьях рассказывают об устройстве документов в монго, которые по сути являются банально денормализованными данными в понятии sql со всеми вытекающими минусами. Единственным отличием от sql является отсутствие схемы.
Отсюда и скорость, ведь если в mysql хранить все данные в одной таблице, то скорость тоже будет очень высокой.
При этом они взахлеб рассказывают о "плюсах" такой денормализации и преподносят это так, как будто это открытие. Получается, что нас всю жизнь учили данные нормализовывать и объясняли, почему это хорошо, а теперь все с точностью наоборот?
Объясните, пожалуйста, на пальцах, правильно ли я все понимаю? Как в монго при этом обновлять данные, которые хранятся в каждом документе? Приемлемо ли в монго денормализовывать данные, чтобы хранить данные в отдельной коллекции, и обращаться к нмм отдельным запросом (ведь джоинов там нет)? И принято ли так работать? Что делать, если изменилась структура данных, если структуры то и нет?
Спасибо.

UPD:
Для ясности, поясните, пожалуйста, как бы вы реализовали структуру в монге, которая в sql выглядит так:
users - пользователи сайта.
authors - авторы статей на сайте.
news - новости, которые публикуют авторы (внешний ключ на авторов).
author_user - связь между пользователями и авторами, пользователи подписываются на своих любимых авторов, чтобы читать их новости (внешний ключ на авторов и на пользователей).
Задача проекта - показывать пользователю новости авторов, на которых он подписан и, собственно, управлять подпиской на авторов.
У меня в голове 2 варианта:
1) Создать 4 коллекции с соответствующими названиями и хранить id нужных сущностей, но получается все тот же sql и выбирать придется из разных таблиц. Полагаю, что это неправильный поход.
2) Хранить в коллекции с постами денормализованные данные об авторах этих статей. Тут понятно, но непонятно, как добавлять пользователей? Ведь сегодня на автора подписано 10 человек, а завтра 100.
Кроме того, например завтра потребуется постам добавить новое поле "жанр", а у всех существующих поменять формат поля "рейтинг новости". Получится, что у новых документов поле жанр будет, а у старых нет? Это придется отслеживать на уровне приложения?
  • Вопрос задан
  • 3301 просмотр
Решения вопроса 2
zoonman
@zoonman
⋆⋆⋆⋆⋆
Я работаю с MongoDB уже более 3 лет, поэтому буду рассказывать и давать советы опираясь на личный опыт эксплуатации.

Получается, что нас всю жизнь учили данные нормализовывать и объясняли, почему это хорошо, а теперь все с точностью наоборот?

Не совсем. Вас учили работать только с одной разновидностью баз данных - реляционной. Теперь вы увидели, что бывают еще и другие, документ-ориентированные. Разумеется, в каждой разновидности будут свои подходы к хранению и организации данных.
Это не хорошо и не плохо, это иначе.
Несомненно, ажиотаж вокруг термина NoSQL существует. И на то есть причины, в основном то, что данных действительно стало больше. Информационная энтропия увеличивается и ее все сложнее укладывать в рамки реляционных баз данных. Здесь можно долго рассуждать, но могу с уверенностью сказать, что сейчас появился спрос на такие хранилища, в которых структуру нужно менять более быстро, чем это могут позволить реляционные базы данных.

Объясните, пожалуйста, на пальцах, правильно ли я все понимаю?

По большей части вы правы. Это денормализованные данные, но с определенными моментами. Я покажу вам их на вашем же примере.

Как в монго при этом обновлять данные, которые хранятся в каждом документе?


Это реализуется с помощью банальных обновлений.
Например, если у меня есть коллекция с книгами, в которой мне нужно обновить авторов.

Типичная запись в ней выглядит так
{
    "_id" : ObjectId("5801aa17964c6b2a050041a7"),
    "title" : "New Book",
    "authors" : [ 
        {
            "_id" : ObjectId("5801aa0f964c6b26030041a9"),
            "firstName" : "Phil",
            "lastName" : "Tkachev"
        }
    ]
}


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

db.getCollection('book').update(
 {'authors._id':ObjectId("5801aa0f964c6b26030041a9")}, 
 {$set: {'authors.$.firstName': 'Philipp'}  }, 
 {multi: true } 
)


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

Приемлемо ли в монго денормализовывать данные, чтобы хранить данные в отдельной коллекции, и обращаться к ним отдельным запросом (ведь джоинов там нет)?


Есть ряд случаев, когда так и делают. Например есть разного рода ORM, тот же Mongoose, который так и делает.

И принято ли так работать?


И да, и нет. Когда вы работаете с такого рода базой данных, вам нужно подходить к организации данных исходя из решаемой проблемы, отталкиваться от проекта будущего приложения или решения задачи.
Вам просто нужно ответить на вопрос, что дешевле, запросить документ по ключу или обновить запись внутри документов.
Взять к примеру, ваш сайт, в котором есть новости и их авторы. Новости могут читать миллионы, а значит при обращении к каждой новости нужно будет делать подзапрос на информацию о каждом авторе. Т.е. вместо одного запроса, при просмотре новости, нужно будет делать 2. А если показывать список из 100 новостей? Будете делать 100 вторичных запросов? Нет, это тоже неправильно. Нужно будет получить список новостей, в коде приложения собрать идентификаторы авторов, сделать второй подзапрос, получить информацию об авторах, затем объединить ее с уже полученным списком статей. Это немного усложнит ваше приложение, но тоже позволит сэкономить ресурсы. Если вы встроите авторов внутрь статьи, это позволит вам обойтись одним запросом к базе, хоть на просмотр, хоть на список новостей. С другой стороны вам прийдется подумать об обновлении информации об авторе. Но, т.к. такая информация меняется сравнительно редко, то есть смысл встраивания.

Что делать, если изменилась структура данных, если структуры то и нет?


Здесь все просто. Когда вы разрабатываете свое приложение, вы изначально закладываете в него обработку изменений. Например, вы можете добавить поле версии документа, в котором храните номер версии структуры и реагировать на ее изменение в коде. Либо вы можете просто писать приложение таким образом, что оно автоматически будет конвертировать структуру из старой в новую при первом обращении.

По дизайну вашего приложения.
Судя по первичным данным, у вас новостной сайт.
Логично было бы его представить в следующем виде.

Коллекция новостей:

{
	_id: 'MongoId',
	title: '',
	body: '',
	author: {
		_id: 'идентификатор пользователя',
		name: 'Имя пользователя',
		subscribers: 'Количество подписчиков'
	}
}


Автор является неполной копией данных о пользователе. Это поможет сэкономить место и позволит избежать ненужных запросов.

Коллекция пользователей:

{
	_id: 'MongoId',
	name: 'Имя пользователя',
	email: '',
	roles: ['user', 'author', 'admin'],
	subscribers: 'Number'
}


Список ролей может опеределять уровень доступных возможностей. Его легко изменить, можно легко найти авторов или админов.

Коллекция подписок:

{
	initiator: {
		_id: 'идентификатор пользователя, который инициировал подписку',
		name: 'Имя пользователя'
	},
	target: {
		_id: 'идентификатор автора',
		name: 'Имя пользователя'
	},
	date: 'ISODate',
	confirmed: 'bool'
}


Здесь вы можете подстроить, как список подписок, так и список подписчиков одним запросом.
Ответ написан
@azShoo
Первое. Данные в монге денормализованы by design.
И тут у вас возникает два пути:
1) "Джойны". Т.к. непосредственных кросс-коллекционных запросов монга не даёт, придется это делать силами обёрток - ORM\фреймворка\датапровайдера.
Это больно и медленно (т.е. есть неиллюзорный шанс что боттлнеком станет как раз склеивание джейсонов на php, а не чтение\запись в базу).

2) Дублирование данных. Условно, у вас есть объемная и активно используемая коллекция новостей и в ней уже по умолчанию содержатся все необходимые данные для отображение списка новостей. В том числе, например, информация об авторе (условно - его айдишник, имя и аватар).
Есть менее жирная и реже используемая коллекция авторов - туда вы лезете, когда хотите полную информацию об авторе.
Но если автор вдруг сменит имя\аватар\пол -> апдейтить придется сразу две коллекции. Соответственно и держать в голове все места дублирования данных при любом изменении хранимых объектов.

В соотношении этих двух путей кроется великое таинство относительно безболезненного использования монги.
Ну, и если в процессе работы вы понимаете, что у вас в данных присутствует связь "всё со всем", то вам надо серьезно задуматься о переходе на реляционные базы данных.
Всё таки NoSQL хранилища - это инструмент, который заточен на хранение и обработку больших объемов данных со слабой связанностью. Запихать туда high-relational базу - это костыли, ад и боль.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@lega
Удобно то что монга позволяет задать нужный уровень (де)нормализации, это позволяет ускорить разработку и производительность приложения.
Джойонов нет, но есть неудобные lookup.
В монге нормально обновляются данные, для этого есть куча разных опций.
Структуру лучше хранить в приложении (хотя в монге можно задать некую валидацию).

Как по мне, для большинства проектов тут не хватает нормальных транзакций, с остальным более-менее норм.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы