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

27

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

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

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

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

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

Спасибо

Ваш Ответ

12   ответов
2

на месте обновления

Например:

http://www.mongodb.org/display/DOCS/Updating#comment-41821928

db.users.update( { level: "Sourcerer" }, { '$push' : { 'inventory' : 'magic wand'} }, false, true );

который будет толкать «волшебную палочку» во все "Sourcerer" массив инвентаря пользователя. Обновление для каждого документа / пользователя является атомарным.

0

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 в своем весеннем контексте:

https://github.com/azee/template-api/blob/master/template-rest/src/main/resources/webContext.xml

5

this& Quot;

"Тогда не делай"do ! Что & Quot;

По сути, то, что вы описываете, звучит так, как будто у вас есть последовательная зависимость - MongoDB или что-то еще, в вашем алгоритме есть точка, в которой операция должна быть сериализована. Это будет врожденным узким местом, и если вы абсолютно обязаны это сделать, вам придется организовать какой-то семафор для его защиты.

Итак, место для поиска - ваш алгоритм. Вы можете устранить это? Не могли бы вы, например, справиться с этим с помощью какого-либо разрешения конфликта, например & quot; получить запись в локальном & apos; Обновить; сохранить запись & quot; так, чтобы после магазина новая запись была получена на этот ключ?

Отличный комментарий. Не делайте этого, даже если это работа, которую вы ДОЛЖНЫ делать, просто потому, что какой-то инструмент недостаточно хорош.
MongoDB finally supports Transactions : Dstackoverflow.com/a/53800048/2757916
@AnatolyAlekseev, если MongoDB этого не сделает, он этого не сделает. И посмотрите на остальную часть того, что я сказал: «Итак, вам стоит взглянуть на ваш алгоритм. Вы можете устранить это? & Quot; Прочитайте весь ответ и не будьте проклятым дураком.
Я сделал. Как трудно может быть попытка не дать какую-то общую мудрость в начале ответа, просто чтобы показать, что автор темы не достаточно умен?
Я, Чарли, спасибо за ответ. Я не понимаю решение конфликта, которое вы предлагаете. Я согласен, что мне нужно изменить свой алгоритм, и я могу представить какое-то решение, но я чувствую, что должно быть какое-то согласованное решение этой проблемы. Мне кажется, что это классическая проблема, с которой сталкиваются многие люди, использующие mongodb (или, возможно, любую базу данных). Если бы это было обновление памяти, я бы знал, как использовать мьютекс для «блокировки». переменная, которую я хочу обновить, чтобы обновлять ее мог только один поток. Наверное, мой вопрос: как другие программисты обычно справляются с этой ситуацией? Mathieu Pagé
0

$ толчок Оператор должен быть достаточно безопасным, чтобы не допустить перезаписи потоков изменениями друг друга.

0

операции для наборов реплик. Поддержка сегментированных кластеров появится в MongoDB 4.2. При использовании транзакций фиксация обновлений БД будет прервана в случае конфликтующей записи, что решит вашу проблему.

Транзакции намного дороже с точки зрения производительности, поэтому не используйте Транзакции как оправдание плохого дизайна схемы NoSQL!

2

потому что я нашел решение, когда проводил исследования в Интернете.

Я думаю, что мне нужно сделать, это использоватьОптимистичный Контроль Конкуренции.

Он состоит в добавлении временной метки, хэша или другого уникального идентификатора (я использовал 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();
}
Вы можете сделать это, но используя атомарные операторы, некоторые из других описанных ответов, вероятно, то, что вы хотите (и атомарно, как вы хотите). Вот документы:mongodb.org/display/DOCS/Atomic+Operations
Да, это один из способов разрешения конфликтов.
12

я думаю, что единственный способ добавить параметр состояния и использовать операциюfindAndModify (), что позволяет вам атомарно изменить документ. Это немного медленнее, но должно сработать.

Так, скажем, вы добавляете атрибут статуса, а когда вы извлекаете документ, меняете статус с «IDLE». на "ОБРАБОТКУ". Затем вы обновляете документ и сохраняете его обратно в коллекцию, обновляя статус до «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 на стороне клиента. Поэтому, когда вы закончите с обновлением, просто сохраните / обновите все снова.

Единственное, что вам нужно знать, это то, что вы можете изменять только один документ за раз.

Надеюсь, поможет.

Как другой клиент ожидает снятия блокировки? то есть как вы можете получать уведомления, когдаstatus изменения?
Обидно, что они не расширили findAndModify () для работы с несколькими документами.
Вы можете использовать простой update () для той же цели, что является официальным решением, предлагаемым на сайте MongoDB:docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations  Основное осложнение этого решения заключается в том, что вы должны написать код для случая, когда обновление не удалось. То есть Повторите обновление. В зависимости от вашего кода вам могут потребоваться дополнительные сложности, чтобы избежать побочных эффектов при повторных попытках и т. Д.
@slezica прав, и вы можете найти решение? Как другой клиент узнает об освобождении заблокированного документа?
Что делать, если я хочу заблокировать при создании объекта документа?
7

работая над обновлениями mongodb. В отличие от того времени, когда был задан этот вопрос, теперь mongodb поддерживает блокировку на уровне документа из коробки.

От:http://docs.mongodb.org/manual/faq/concurrency/

& quot; Насколько гранулированы блокировки в MongoDB?

Изменено в версии 3.0.

Начиная с версии 3.0, MongoDB поставляется с механизмом хранения WiredTiger, который использует оптимистичный контроль параллелизма для большинства операций чтения и записи. WiredTiger использует только намеренные блокировки на глобальном уровне, уровне базы данных и уровне сбора. Когда механизм хранения обнаруживает конфликты между двумя операциями, одна из них вызывает конфликт записи, в результате чего MongoDB прозрачно повторяет эту операцию. & Quot;

Что делать, если я хочу заблокировать при создании объекта документа?
3

когда вы хотите сделать что-то поточно-ориентированное, - это использовать блокировки (мьютексы). Это также называетсяpessimistic locking в отличие отoptimistic locking описаноВот.

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

MongoDB не предоставляет механизм блокировки. Но это может быть легко реализовано на уровне приложения (т. Е. В вашем коде):

Acquire lock Read document Modify document Write document Release lock

Степень детализации блокировки может быть различной: глобальная, специфичная для коллекции, специфичная для записи / документа. Чем конкретнее блокировка, тем меньше ее производительность.

Это не работает в приложении с несколькими экземплярами.
Действие получения блокировки обычно ожидает блокировки, если она удерживается другим потоком.
Как ты ждешь на замке?
2

Update: тве механизма по умолчанию MongoDB использует блокировку по умолчанию на уровне документа. Она была введена в версии 3.0, но по умолчанию установлена в версии 3.2.2. Поэтому MongoDB теперь имеет блокировку на уровне документа.

0

я пытаюсь ответить на этот вопрос: мне интересно, справится ли это хранилище WiredTiger с проблемой, которую я указал здесь: Предельные вставки в mongodb

-1
Проблема с атомарными операторами состоит в том, что они действительно не помогают мне, так как я использовал шаблоны репозитория, поэтому у меня были только операции CRUD. Mathieu Pagé

Похожие вопросы