Невозможно заблокировать документ mongodb. Что, если мне нужно?
Я знаю, что я не могу заблокировать один файл mongodb, на самом деле нет возможности блокировать коллекцию.
Однако у меня есть этот сценарий, где мне кажется, что мне нужно каким-то образом предотвратить изменение одного документа (или процесса, неважно) от изменения документа. Вот мой сценарий.
У меня есть коллекция, содержащая объект типа A. У меня есть код, который извлекает документ типа A, добавляет элемент в массив, который является свойством документа (a.arr.add(new Thing()
), а затем сохраняет документ назад to mongodb. Этот код параллелен, несколько потоков в моих приложениях могут выполнять тезисы операций, и пока нет способа предотвратить потоки от параллельного выполнения тезисов в одном документе. Это плохо, потому что один из потоков может перезаписать работы другого.
Я использую шаблон репозитория для абстрагирования доступа к коллекции mongodb, поэтому у меня есть только операции CRUD в моем расположении.
Теперь, когда я думаю об этом, возможно, это ограничение шаблона репозитория, а не ограничение mongodb, которое вызывает у меня проблемы. Во всяком случае, как я могу сделать этот код "потокобезопасным"? Я думаю, там хорошо известное решение этой проблемы, но, будучи новым для mongodb и шаблона репозитория, я не сразу его вижу.
Спасибо
Ответы
Ответ 1
Наткнулся на этот вопрос, работая над модернизацией mongodb. В отличие от того, как задавался этот вопрос, теперь mongodb поддерживает блокировку уровня документа из коробки.
От: http://docs.mongodb.org/manual/faq/concurrency/
"Насколько гранулированы блокировки в MongoDB?
Изменено в версии 3.0.
Начиная с версии 3.0, MongoDB поставляется с механизмом хранения WiredTiger, который использует оптимистичный элемент управления concurrency для большинства операций чтения и записи. WiredTiger использует только блокировки намерений на глобальном уровне, уровне базы данных и сбора. Когда механизм хранения обнаруживает конфликты между двумя операциями, возникает конфликт записи, заставляющий MongoDB прозрачно повторять эту операцию. "
Ответ 2
Привет, единственный способ, с которого я думаю, теперь добавить параметр статуса и использовать операцию findAndModify(), которая позволяет вам атомизировать изменить документ. Он немного медленнее, но должен сделать трюк.
Итак, скажем, вы добавляете атрибут статуса, и когда вы извлекаете документ, измените статус с "IDLE" на "PROCESSING". Затем вы обновляете документ и сохраняете его обратно в коллекцию, обновляя статус до "IDLE".
Пример кода:
var doc = db.runCommand({
"findAndModify" : "COLLECTION_NAME",
"query" : {"_id": "ID_DOCUMENT", "status" : "IDLE"},
"update" : {"$set" : {"status" : "RUNNING"} }
}).value
Измените COLLECTION_NAME и ID_DOCUMENT на правильное значение. По умолчанию findAndModify() возвращает старое значение, что означает, что значение статуса будет еще IDLE на стороне клиента. Поэтому, когда вы закончите с обновлением, просто сохраните/обновите все снова.
Единственное, что вам нужно знать, это то, что вы можете изменять только один документ за раз.
Надеюсь, что это поможет.
Ответ 3
"Доктор, мне больно, когда я это делаю"
"Тогда не делай этого!"
В основном, то, что вы описываете, похоже на то, что у вас есть последовательная зависимость там - MongoDB или что-то еще, ваш алгоритм имеет точку, в которой операция должна быть сериализована. Это будет неотъемлемым узким местом, и если вы абсолютно это сделаете, вам нужно будет скомпоновать какой-то семафор, чтобы защитить его.
Итак, место для поиска - это ваш алгоритм. Вы можете устранить это? Могли бы вы, например, справиться с каким-то разрешением конфликтов, например, "получить запись в локальное" обновление, сохранить запись ", чтобы после хранения новая запись была получена на этом ключе?
Ответ 4
Классическое решение, когда вы хотите сделать что-то потокобезопасное, - это использовать блокировки (мьютексы).
Это также называется пессимистической блокировкой, а не оптимистической блокировкой, описанной здесь.
Есть сценарии, когда пессимистическая блокировка более эффективна (подробнее здесь). Это также намного проще реализовать (основная трудность оптимистической блокировки - это восстановление от столкновения).
MongoDB не обеспечивает механизм блокировки. Но это можно легко реализовать на уровне приложения (т.е. В вашем коде):
- Приобретение блокировки
- Прочитать документ
- Изменить документ
- Записать документ
- Блокировка блокировки
Гранулярность блокировки может быть различной: глобальная, специфичная для коллекции, запись/документация. Чем точнее блокировка, тем меньше ее штраф за производительность.
Ответ 5
Отвечая на мой собственный вопрос, потому что я нашел решение во время исследований в Интернете.
Я думаю, что мне нужно сделать Оптимистичный контроль соответствия.
Он состоит в добавлении метки времени, хэша или другого уникального идентификатора (я буду использовать UUID) для каждого документа. Уникальный идентификатор должен быть изменен при каждом изменении документа. перед обновлением документа я сделаю что-то вроде этого (в псевдокоде):
var oldUUID = doc.uuid;
doc.uuid = new UUID();
BeginTransaction();
if (GetDocUUIDFromDatabase(doc.id) == oldUUID)
{
SaveToDatabase(doc);
Commit();
}
else
{
// Document was modified in the DB since we read it. We can't save our changes.
RollBack();
throw new ConcurencyException();
}
Ответ 6
Альтернативой является обновление на месте
для ex:
http://www.mongodb.org/display/DOCS/Updating#comment-41821928
db.users.update( { level: "Sourcerer" }, { '$push' : { 'inventory' : 'magic wand'} }, false, true );
который будет подталкивать "волшебную палочку" во все массивы инвентаризации "Sourcerer". Обновление для каждого документа/пользователя является атомарным.
Ответ 7
Update:
С MongoDB 3.2.2 с использованием реализации WiredTiger Storage в качестве механизма по умолчанию MongoDB использует блокировку по умолчанию на уровне документа. Он был представлен в версии 3.0, но был установлен по умолчанию в версии 3.2.2. Поэтому MongoDB теперь имеет блокировку уровня документа.
Ответ 8
Если у вас есть система s > 1 сервером, вам понадобится дистрибутивный замок.
Я предпочитаю использовать Hazelcast.
Во время сохранения вы можете получить блокировку Hazelcast по идентификатору объекта, получить и обновить данные, затем отпустить блокировку.
В качестве примера:
https://github.com/azee/template-api/blob/master/template-rest/src/main/java/com/mycompany/template/scheduler/SchedulerJob.java
Просто используйте lock.lock()
вместо lock.tryLock()
Здесь вы можете увидеть, как настроить Hazelcast в контексте spring:
https://github.com/azee/template-api/blob/master/template-rest/src/main/resources/webContext.xml
Ответ 9
Вместо того, чтобы писать вопрос в другом вопросе, я пытаюсь ответить на этот вопрос: мне интересно, справится ли это WiredTiger Storage с проблемой, которую я указал здесь:
Предельные вставки в mongodb
Ответ 10
Похоже, вы хотите использовать атомные операторы MongoDB: http://www.mongodb.org/display/DOCS/Atomic+Operations