Подтвердить что ты не робот

Невозможно заблокировать документ mongodb. Что, если мне нужно?

Я знаю, что я не могу заблокировать один файл mongodb, на самом деле нет возможности блокировать коллекцию.

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

У меня есть коллекция, содержащая объект типа A. У меня есть код, который извлекает документ типа A, добавляет элемент в массив, который является свойством документа (a.arr.add(new Thing()), а затем сохраняет документ назад to mongodb. Этот код параллелен, несколько потоков в моих приложениях могут выполнять тезисы операций, и пока нет способа предотвратить потоки от параллельного выполнения тезисов в одном документе. Это плохо, потому что один из потоков может перезаписать работы другого.

Я использую шаблон репозитория для абстрагирования доступа к коллекции mongodb, поэтому у меня есть только операции CRUD в моем расположении.

Теперь, когда я думаю об этом, возможно, это ограничение шаблона репозитория, а не ограничение mongodb, которое вызывает у меня проблемы. Во всяком случае, как я могу сделать этот код "потокобезопасным"? Я думаю, там хорошо известное решение этой проблемы, но, будучи новым для mongodb и шаблона репозитория, я не сразу его вижу.

Спасибо

4b9b3361

Ответ 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