Как MongoDB справляется с транзакционными конфликтами? - PullRequest
0 голосов
/ 08 ноября 2018

Если оба потока читают и пишут один и тот же документ:

try (ClientSession clientSession = client.startSession()) {
    clientSession.startTransaction();
    result = collection.find(clientSession, keyOfDoc);
    if (result blah blah blah) {
        // Change the doc
        collection.insertOne(clientSession, doc);
    }
    clientSession.commitTransaction();
}

Исходя из цели транзакции, один из потоков должен получить отредактированную версию другого потока.

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

Другая ситуация - конфликт записи-записи.

try (ClientSession clientSession = client.startSession()) {
    clientSession.startTransaction();
    collection.insertOne(clientSession, docDifferent);
    collection.insertOne(clientSession, docSame);
    clientSession.commitTransaction();
}

Оба потока сначала получают блокировки записи разных документов, а затем они получают блокировку записи одного и того же документа, поскольку это еще один транзакционный конфликт.

Какой уровень блокировки использует MongoDB? Я знаю, что они используют уровень экземпляра до версии 2.2, в то время как транзакции поддерживаются начиная с 4.0. Если MongoDB не использует блокировки на уровне базы данных, как MongoDB справляется с транзакционными конфликтами? Или, если он использует блокировки на уровне базы данных, как он справляется с конфликтами чтения-записи?

1 Ответ

0 голосов
/ 09 ноября 2018

Я нашел несколько ссылок в руководстве MongoDB, которые решили мой собственный вопрос.

Какой тип блокировки использует MongoDB?

MongoDB использует многофакторную блокировку 1 , которая позволяет блокировать операции на глобальном уровне, уровне базы данных или коллекции, а также позволяет отдельным механизмам хранения реализовать собственный контроль параллелизма ниже уровня сбора (например, при уровень документа в WiredTiger).

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

Запрещенные операции

Следующие операции не разрешены в многодокументных транзакциях:

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

    Команды listCollections и listIndexes и их вспомогательные методы также исключаются.

  • Не CRUD и неинформационные операции, такие как createUser, getParameter, count и т. Д. И их помощники.

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

Повторить транзакцию

Отдельные операции записи внутри транзакция не повторяется, независимо от того, является ли retryWrites установить на true.

Если операция обнаруживает ошибку, возвращаемая ошибка может иметь Поле массива errorLabels. Если ошибка является временной ошибкой, Поле массива errorLabels содержит «TransientTransactionError» как элемент и транзакция в целом могут быть повторены.

Значение, когда посетитель получает MongoException, а исключение .hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL), , посетитель должен закрыть сеанс и отменить транзакцию . Посетитель должен повторить и подтвердить, пока фиксация не будет успешной.

Вы можете просто использовать этот метод (модифицированный из ручного примера):

public static <T> T transactWithRetry(Callable<T> transactional) throws Exception {
    while (true) {
        try {
            return transactional.call();
        } catch (MongoException ex) {
            if (!ex.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL)) throw ex;
        }
    }
}

Дополнительные издания см. В руководствах ;)!


Ссылки

Транзакции & mdash; MongoDB Manual

FAQ: параллелизм & mdash; MongoDB Manual

...