Могу ли я выполнять транзакции и блокировки в CouchDB?

StackOverflow https://stackoverflow.com/questions/299723

  •  08-07-2019
  •  | 
  •  

Вопрос

Мне нужно выполнить транзакции (начать, зафиксировать или откатить), блокировки (выбрать для обновления).Как я могу сделать это в базе данных модели документа?

Редактировать:

Дело в следующем:

  • Я хочу запустить сайт аукционов.
  • И я тоже думаю, как сделать прямую закупку.
  • При прямой покупке мне приходится уменьшать поле количества в записи товара, но только если количество больше нуля.Вот почему мне нужны блокировки и транзакции.
  • Я не знаю, как решить эту проблему без блокировок и/или транзакций.

Могу ли я решить эту проблему с помощью CouchDB?

Это было полезно?

Решение

Нет.CouchDB использует модель «оптимистического параллелизма».Проще говоря, это просто означает, что вы отправляете версию документа вместе с обновлением, а CouchDB отклоняет изменение, если текущая версия документа не соответствует той, которую вы отправили.

На самом деле это обманчиво просто.Вы можете переосмыслить многие обычные сценарии, основанные на транзакциях, для CouchDB.Однако при изучении CouchDB вам придется отказаться от своих знаний в области РСУБД.Полезно подходить к проблемам с более высокого уровня, а не пытаться приспособить Couch к миру, основанному на SQL.

Отслеживание инвентаря

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

  1. Получите документ, обратите внимание на _rev свойство, которое CouchDB отправляет вместе
  2. Уменьшите поле количества, если оно больше нуля.
  3. Отправьте обновленный документ обратно, используя _rev свойство
  4. Если _rev соответствует текущему сохраненному номеру, готово!
  5. Если возник конфликт (когда _rev не совпадает), получить новейшую версию документа

В этом случае следует рассмотреть два возможных сценария отказа.Если количество последней версии документа равно 0, вы обрабатываете ее так же, как в СУБД, и предупреждаете пользователя о том, что он на самом деле не может купить то, что хотел.Если самая последняя версия документа имеет количество больше 0, вы просто повторяете операцию с обновленными данными и начинаете с начала.Это заставляет вас выполнять немного больше работы, чем СУБД, и может немного раздражать, если происходят частые конфликтующие обновления.

Ответ, который я только что дал, предполагает, что вы будете делать что-то в CouchDB почти так же, как и в СУБД.Я мог бы подойти к этой проблеме немного иначе:

Я бы начал с документа «основного продукта», который включает все дескрипторные данные (имя, изображение, описание, цену и т. д.).Затем я бы добавил документ «Инвентарная квитанция» для каждого конкретного экземпляра с полями для product_key и claimed_by.Если вы продаете модель молотка и у вас есть 20 таких молотков, у вас могут быть документы с ключами, например hammer-1, hammer-2, и т. д., чтобы представить каждый доступный молоток.

Затем я бы создал представление, которое дает мне список доступных молотков, с функцией уменьшения, которая позволяет мне видеть «общее количество».Это совершенно спонтанно, но должно дать вам представление о том, как будет выглядеть рабочее представление.

карта

function(doc) 
{ 
    if (doc.type == 'inventory_ticket' && doc.claimed_by == null ) { 
        emit(doc.product_key, { 'inventory_ticket' :doc.id, '_rev' : doc._rev }); 
    } 
}

Это дает мне список доступных «билетов» по ​​ключу продукта.Я мог бы взять группу таких сообщений, когда кто-то захочет купить молоток, а затем выполнить итерацию по отправке обновлений (используя id и _rev), пока я не заявлю один из них (ранее заявленные билеты приведут к ошибке обновления).

Уменьшать

function (keys, values, combine) {
    return values.length;
}

Эта функция уменьшения просто возвращает общее количество невостребованных inventory_ticket предметов, чтобы вы могли узнать, сколько «молотов» можно приобрести.

Предостережения

Это решение представляет собой примерно 3,5 минуты общего обдумывания конкретной проблемы, которую вы представили.Возможно, есть лучшие способы сделать это!Тем не менее, это существенно уменьшает количество конфликтующих обновлений и снижает необходимость реагировать на конфликт новым обновлением.В рамках этой модели у вас не будет нескольких пользователей, пытающихся изменить данные в основной записи продукта.В худшем случае несколько пользователей попытаются получить один билет, и если вы уловили несколько из них, вы просто переходите к следующему билету и пытаетесь еще раз.

Ссылка: https://wiki.apache.org/couchdb/Frequly_asked_questions#How_do_I_use_transactions_with_CouchDB.3F

Другие советы

Расширение ответа г-на Курта.Во многих сценариях вам не обязательно погашать биржевые билеты по порядку.Вместо того, чтобы выбирать первый билет, вы можете выбирать случайным образом из оставшихся билетов.Учитывая большое количество билетов и большое количество одновременных запросов, вы получите гораздо меньше конфликтов по этим билетам по сравнению со всеми, кто пытается получить первый билет.

Шаблон проектирования спокойных транзакций заключается в создании «напряжения» в системе.В популярном примере использования транзакции по банковскому счету вы должны обязательно обновить общую сумму для обоих участвующих счетов:

  • Создайте документ операции «перевод 10 долларов США со счета 11223 на счет 88733».Это создает напряжение в системе.
  • Чтобы устранить любое сканирование напряжённости для всех документов по транзакциям и
    • Если исходная учетная запись еще не обновлена, обновите исходную учетную запись (-10 долларов США).
    • Если исходный счет был обновлен, но в документе транзакции это не отображается, обновите документ транзакции (например,установите флаг «sourcedone» в документе)
    • Если целевая учетная запись еще не обновлена, обновите целевую учетную запись (+10 долларов США)
    • Если целевой счет был обновлен, но в документе транзакции это не отображается, обновите документ транзакции.
    • Если обе учетные записи были обновлены, вы можете удалить документ транзакции или сохранить его для проверки.

Сканирование на наличие напряженности должно выполняться во время внутреннего процесса для всех «документов о напряженности», чтобы сократить время напряженности в системе.В приведенном выше примере на короткое время ожидается несогласованность, когда первая учетная запись была обновлена, а вторая еще не обновлена.Это необходимо учитывать так же, как вы будете обеспечивать конечную согласованность, если ваша Couchdb является распределенной.

Другая возможная реализация полностью исключает необходимость транзакций:просто сохраните документы о напряжении и оцените состояние вашей системы, оценивая каждый задействованный документ о напряжении.В приведенном выше примере это означало бы, что итоговая сумма по счету определяется только как значения суммы в документах транзакции, в которых задействован этот счет.В Couchdb вы можете очень хорошо смоделировать это как представление карты/уменьшения.

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

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

Если вы можете гарантировать, что у вас есть только один экземпляр CouchDB или что все, кто изменяет определенный документ, подключаются к одному и тому же экземпляру CouchDB, тогда вы можете использовать систему обнаружения конфликтов для создания своего рода атомарности, используя методы, описанные выше, но если вы позже масштабируетесь до кластера. или используйте размещенный сервис, такой как Cloudant, он выйдет из строя, и вам придется переделывать эту часть системы.

Итак, я предлагаю использовать для баланса вашего аккаунта что-то другое, кроме CouchDB, так будет намного проще.

В качестве ответа на проблему ОП, Couch, вероятно, здесь не лучший выбор.Использование представлений — отличный способ отслеживать запасы, но ограничить их значением до 0 более или менее невозможно.Проблема заключается в состоянии гонки, когда вы читаете результат представления, решаете, что можете использовать элемент «hammer-1», а затем пишете документ для его использования.Проблема в том, что не существует атомарного способа написать документ, использующий молоток, только если в результате представления имеется > 0 молотков-1.Если 100 пользователей одновременно запрашивают представление и видят 1 молоток-1, все они могут написать документ, использующий молоток 1, что приведет к -99 молоткам-1.На практике состояние гонки будет довольно небольшим - очень маленьким, если ваша БД работает на локальном хосте.Но как только вы масштабируетесь и получаете удаленный сервер или кластер БД, проблема станет гораздо более заметной.Тем не менее, недопустимо иметь состояние гонки такого рода в критически важной системе, связанной с деньгами.

Обновление ответа MrKurt (возможно, оно просто датировано, или он мог не знать о некоторых функциях CouchDB)

Представление — это хороший способ обрабатывать такие вещи, как балансы/запасы в CouchDB.

Вам не нужно указывать docid и rev в представлении.Вы получаете оба из них бесплатно при получении результатов просмотра.Их создание - особенно в подробном формате, таком как словарь - просто увеличит ваше представление неоправданно.

Простое представление для отслеживания остатков запасов должно выглядеть примерно так (тоже пришло мне в голову)

function( doc )
{
    if( doc.InventoryChange != undefined ) {
        for( product_key in doc.InventoryChange ) {
            emit( product_key, 1 );
        }
    }
}

А функция уменьшения еще проще

_sum

Для этого используется встроенная функция уменьшения это просто суммирует значения всех строк с совпадающими ключами.

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

{
    "_id": "abc123",
    "InventoryChange": {
         "hammer_1234": 10,
         "saw_4321": 25
     }
}

Добавил бы 10 молотков_1234 и 25 пил_4321.

{
    "_id": "def456",
    "InventoryChange": {
        "hammer_1234": -5
    }
}

Сожжет 5 молотков из инвентаря.

Используя эту модель, вы никогда не обновляете данные, а только добавляете их.Это означает, что нет возможности конфликтов обновлений.Все транзакционные проблемы обновления данных уходят :)

Еще одна приятная особенность этой модели заключается в том, что ЛЮБОЙ документ в БД может как добавлять, так и вычитать элементы из инвентаря.Эти документы могут содержать любые другие данные.У вас может быть документ «Отгрузка» с набором данных о дате и времени получения, складе, принимающем сотруднике и т. д.и пока этот документ определяет InventoryChange, он будет обновлять инвентарь.Как и документ «Продажа», документ «Поврежденный предмет» и т. д.Глядя на каждый документ, они читаются очень четко.И представление справляется со всей тяжелой работой.

На самом деле, в некотором смысле, вы можете.Взгляните на API HTTP-документов и прокрутите вниз до заголовка «Изменение нескольких документов одним запросом».

По сути, вы можете создать/обновить/удалить несколько документов в одном запросе на публикацию. URI /{dbname}/_bulk_docs и они либо все добьются успеха, либо все потерпят неудачу.Однако в документе предупреждается, что такое поведение может измениться в будущем.

РЕДАКТИРОВАТЬ:Как и предполагалось, начиная с версии 0.9, массовая обработка документов больше не работает таким образом.

Просто используйте облегченное решение SQlite для транзакций, а когда транзакция будет успешно реплицирована, и отметьте ее как реплицированную в SQLite.

таблица SQLite

txn_id    , txn_attribute1, txn_attribute2,......,txn_status
dhwdhwu$sg1   x                    y               added/replicated

Вы также можете удалить транзакции, которые успешно реплицируются.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top