2014-09-09 3 views
1

Я раньше не использовал CouchDB/MongoDB/Couchbase и оцениваю их для своего приложения. Вообще говоря, они кажутся очень интересной технологией, которую я бы хотел использовать. Однако, исходя из опыта РСУБД, я не согласен с отсутствием транзакций. Но в то же время я знаю, что будет гораздо меньше необходимости в транзакциях, как это было бы в РСУБД, учитывая способ организации данных.CouchDB/Couchbase/MongoDB эмуляция транзакций?

При этом у меня есть следующее требование и не уверен, что/как я могу использовать базу данных NoSQL.

  1. У меня есть список клиентов
  2. Каждый клиент может иметь несколько файлов
  3. Каждый файл должен быть последовательно номер для конкретного клиента

Учитывая РСУБД это будет довольно просто. Одна таблица для клиента, одна (или более) для файлов. В таблице клиентов держите счетчик последнего номера файла и увеличивайте его на единицу при вставке новой записи в таблицу файлов. Оберните все в транзакцию, и вы уверены, что есть несоответствия. Черт, просто чтобы быть в безопасности, я мог бы даже установить уникальное ограничение на индекс (clientId, filenumber), чтобы гарантировать, что никогда не будет одинакового имени файла, используемого дважды для клиента.

Как я могу добиться чего-то подобного в MongoDB или CouchDB/base? Возможно ли это? Я продолжаю читать о двухфазных коммитах, но я, похоже, не могу оборачивать голову тем, как это работает в этом экземпляре. Есть ли что-нибудь в Spring/Java, которое обеспечивает двухфазную фиксацию, которая будет работать с этими БД, или это должен быть пользовательский код?

ответ

2

Да, вы можете сделать то же самое с MongoDB и Couchbase/CouchDB, используя правильный подход.

Прежде всего в MongoDB у вас есть уникальный индекс, это поможет обеспечить часть проблемы: - http://docs.mongodb.org/manual/tutorial/create-a-unique-index/

Вы также имеете некоторый шаблон для реализации последовательности правильно: - http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/

У вас есть много вариантов для осуществления транзакций с перекрестными документами/сборами, вы можете найти хорошую информацию об этом в этом сообщении в блоге: http://edgystuff.tumblr.com/post/93523827905/how-to-implement-robust-and-scalable-transactions (2-фазный фиксатор подробно описан здесь: http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/)

Так вы говорите о Couchbase, вы можете найти образец здесь: http://docs.couchbase.com/couchbase-devguide-2.5/#providing-transactional-logic

+0

Я уже читал о двухфазных фиксациях, но я до сих пор не вижу, как это применимо здесь. Если у меня есть один поток, я вижу, что он работает, но если у меня есть несколько потоков, у меня нет гарантии на атомарность - это нить # 2 может быть полностью исполнена до того, как нить # 1 будет завершена, а если первый не удастся, второй будет успешным, номер файла/счетчик будет неправильным. Если я не понимаю, как правильно выполнить двухфазную фиксацию (я так не думаю). –

3

CouchDB является транзакционной по умолчанию. Каждый документ в couchdb содержит ключ _rev. Все изменения в документе выполняются в отношении этого ключа _rev: -

  1. Получить документ.
  2. Отправьте его для обновления с использованием свойства _rev.
  3. Если обновление прошло успешно, вы обновили последний документ документа
  4. Если обновление не удалось, документ был не последним. Повторите шаги 1-3.

Отъезд this answer by MrKurt для получения более подробного объяснения.

У couchdb recipies есть пример банковского обслуживания, показывающий, как транзакции выполняются в couchdb.

И есть также эта статья atomic bank transfers, которая иллюстрирует транзакции в couchdb.

В любом случае общая тема во всех этих ссылках заключается в том, что если вы будете следовать шаблону couchdb для обновления с _rev, у вас не может быть несогласованного состояния в вашей базе данных.

Heck, чтобы быть в безопасности, я мог бы даже установить уникальное ограничение на индекс (clientId, filenumber), чтобы гарантировать, что никогда не будет одинакового имени файла, используемого дважды для клиента.

Все документы couchdb уникальны, поскольку поля в двух документах не могут быть одинаковыми. Проверьте view cookbook

Это простой: в базе данных CouchDB каждый документ должен иметь уникальное поле _id. Если вам нужны уникальные значения в базе данных, просто назначьте их в поле _id документа, а CouchDB обеспечит уникальность для вас.

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

Редактировать основанный на комментарий

В случае, когда вы хотите, чтобы увеличить поле в одном документе на основе успешной вставки другого документа

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

{_id:'some_id','count':1}

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

Я всегда заканчиваю случай, когда неудачная вставка файла оставила БД в несогласованном состоянии, особенно с другим клиентом, успешно вставляющим файл в одно и то же время.

Хорошо, так что я уже описал, как вы можете сделать обновления более отдельных документов, но даже при обновлении одного документа вы можете избежать несогласованности, если вы:

  1. Вставьте новый файл
  2. Когда CouchDB дает сообщение успеха -> попытка обновления счетчика.

Почему это работает?

Это работает, потому что, когда вы пытаетесь обновить update document, вы должны поставить строку _rev. Вы можете думать о _rev как о локальном состоянии для своего документа. Рассмотрим этот сценарий: -

  1. Вы читаете документ, который необходимо обновить.
  2. Вы меняете некоторые поля.
  3. Между тем другой запрос уже изменил исходный документ. Это означает, что документ теперь имеет новый
  4. Но вы запрашиваете couchdb для обновления документа с _rev, который является stale, который вы читаете на шаге 1.
  5. Couchdb генерирует исключение.
  6. Вы снова читаете документ, чтобы получить последние _rev и попытайтесь его обновить.

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

Примечание:

Как отметил Даниил _rev правил не распространяются на насыпные обновления.

+2

«Couchdb является транзакционным по умолчанию». - просто уточнить ... Это за документ. Не охватывать несколько документов. Вы можете иметь массовые обновления, которые обрабатываются как «один», если обработчик проверки недействителен, но это не транзакция. – Daniel

+0

Да. Спасибо :) Я забыл о массовых обновлениях. –

+0

Я читал статьи об атомных банковских переводах до и хотя использовал их, но они не обрабатывают случай, когда вам нужно сохранить точный счет. В случае, когда вы хотите увеличить поле в одном документе на основе успешной вставки другого документа, я не могу видеть, как будет работать пример банковского перевода. Независимо от того, как я пытаюсь его структурировать, я всегда заканчиваю случай, когда неудачная вставка файла оставила БД в несогласованном состоянии, особенно когда другой клиент успешно вставлял файл одновременно. –

Смежные вопросы