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

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

Edit:

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

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

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

Ответ 1

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

Это обманчиво просто, на самом деле. Вы можете пересмотреть многие обычные сценарии транзакций для CouchDB. Однако при изучении CouchDB вам нужно разбросать знания домена RDBMS. Это полезно для решения проблем с более высокого уровня, вместо того, чтобы пытаться формировать Couch в мире на основе SQL.

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

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

  • Извлеките документ, обратите внимание на свойство _rev, которое CouchDB отправляет по
  • Уменьшить поле количества, если оно больше нуля
  • Отправьте обновленный документ обратно, используя свойство _rev
  • Если _rev соответствует текущему номеру, сделайте это!
  • Если конфликт (если _rev не соответствует), извлеките самую новую версию документа

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

Теперь ответ, который я только что дал, предполагает, что вы будете делать что-то в 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/Frequently_asked_questions#How_do_I_use_transactions_with_CouchDB.3F

Ответ 2

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

Ответ 3

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

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

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

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

Ответ 4

Как ответ на проблему OP, Couch, вероятно, не самый лучший выбор здесь. Использование представлений - отличный способ отслеживать инвентарь, но прижать к 0 более или менее невозможно. Проблема заключается в состоянии гонки, когда вы читаете результат обзора, решите, что вы можете использовать элемент "молоток-1", а затем написать документ, чтобы использовать его. Проблема заключается в том, что нет атомного способа писать документ только для использования молотка, если результат обзора состоит в том, что есть > 0 молот-1. Если 100 пользователей все одновременно запрашивают представление и видят 1 молот-1, все они могут написать документ, чтобы использовать молоток 1, в результате чего -99 молот-1. На практике состояние гонки будет довольно маленьким - очень мало, если ваша БД работает с localhost. Но как только вы масштабируете и имеете сервер БД или кластеры, проблема станет намного заметнее. Несмотря на это, неприемлемо иметь такое состояние гонки в системе, связанной с критическими деньгами.

Обновление ответа г-на Курта (может быть, оно устарело, или он, возможно, не знал о некоторых функциях 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 hammer_1234 и 25 saw_4321's.

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

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

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

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

Ответ 5

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

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

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

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

Ответ 6

Собственно, вы можете в некотором роде. Посмотрите HTTP Document API и прокрутите вниз до заголовка "Изменение нескольких документов с помощью одного запроса".

В основном вы можете создавать/обновлять/удалять кучу документов в одном почтовом запросе для URI/{dbname}/_ bulk_docs, и они либо все преуспеют, либо все сбой. В документе действительно предостерегают, что это может измениться в будущем.

EDIT: Как и было предсказано, начиная с версии 0.9 объемные документы больше не работают таким образом.

Ответ 7

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

Таблица SQLite

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

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