Операции с документами MongoDB являются атомарными и изолированными, но последовательны ли они? - PullRequest
17 голосов
/ 10 ноября 2011

Я в процессе переноса приложения из хранилища данных App Engine в бэкэнд MongoDB и у меня возник вопрос относительно согласованности «обновлений документов». Я понимаю, что все обновления в одном документе являются атомарными и изолированными, но есть ли способ гарантировать их "согласованность" в разных наборах реплик?

В нашем приложении многие пользователи могут (и будут) пытаться обновить один документ одновременно, вставив в него несколько встроенных документов (объектов) во время одного обновления. Мы должны обеспечить, чтобы эти обновления происходили логически согласованным образом во всех репликах, т.е. когда один пользователь «помещает» несколько встроенных документов в родительский документ, другие пользователи не могут помещать свои встроенные документы в родительский документ, пока мы не убедимся, что они читать и получать обновления первого пользователя.

Итак, что я имею в виду под согласованностью, так это то, что нам нужен способ гарантировать, что если два пользователя попытаются выполнить обновление для одного документа в точно в одно и то же время, MongoDB разрешит запускать только одно из этих обновлений через, и отбрасывает другой (или, по крайней мере, предотвращает возникновение обоих). Мы не можем использовать стандартное решение «шардинга» здесь, потому что одно обновление состоит не только из увеличения или уменьшения.

Каков наилучший способ обеспечения согласованности одного конкретного документа?

Ответы [ 2 ]

19 голосов
/ 10 ноября 2011

Для этого могут быть другие способы, но один из подходов заключается в том, чтобы создавать версии ваших документов и выпускать обновления только для той версии, которую пользователь ранее прочитал (т. Е. Убедиться, что никто другой не обновил документ с момента его последнего использования).читать).Вот краткий пример этой техники с использованием pymongo:

>>> db.foo.save({'_id': 'a', 'version': 1, 'things': []}, safe=True)
'a'
>>> db.foo.update({'_id': 'a', 'version': 1}, {'$push': {'things': 'thing1'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': True, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 1}

Обратите внимание, что ключ "n" равен 1, указывая, что документ был обновлен

>>> db.foo.update({'_id': 'a', 'version': 1}, {'$push': {'things': 'thing2'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': False, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 0}

здесь, где мы пыталисьдля обновления с неверной версией ключ "n" равен 0

>>> db.foo.update({'_id': 'a', 'version': 2}, {'$push': {'things': 'thing2'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': True, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 1}
>>> db.foo.find_one()
{'things': ['thing1', 'thing2'], '_id': 'a', 'version': 3}

Обратите внимание, что этот метод основан на использовании безопасных записей, в противном случае мы не получим подтверждение, указывающее количество обновленных документов.Вариант этого будет использовать команду findAndModify, которая будет либо возвращать документ, либо None (в Python), если документ, соответствующий запросу, не найден.findAndModify позволяет вернуть либо новую (т.е. после применения обновлений), либо старую версию документа.

3 голосов
/ 11 ноября 2011

MongoDB не предлагает репликацию мастер-мастер или многоверсионный параллелизм.Другими словами, записи всегда отправляются на один и тот же сервер в наборе реплик.По умолчанию даже чтение из вторичных серверов отключено, поэтому поведение по умолчанию заключается в том, что вы одновременно общаетесь только с одним сервером.Поэтому вам не нужно беспокоиться о противоречивых результатах в безопасном режиме, если вы используете атомарные модификаторы (например, $inc, $push и т. Д.).

Если вы не хотите ограничивать себя этими атомными модификаторами, сравнитеи обмен в соответствии с рекомендациями dcrosta (и mongo docs ) выглядит хорошей идеей.Все это не относится к наборам реплик или шардингу, однако - это будет то же самое в сценарии с одним сервером .

Если вам необходимо обеспечить согласованность чтения и в случае базы данных/ node fail, вы должны убедиться, что вы пишете на большинство серверов в безопасном режиме.

Два подхода ведут себя по-разному, если вы разрешаете небезопасное чтение : атомарные операции обновления все равно будутработать (но может дать неожиданные результаты), в то время как подход сравнения и обмена потерпит неудачу.

...