code

CouchDB에서 트랜잭션과 잠금을 할 수 있습니까?

codestyles 2020. 10. 9. 11:13
반응형

CouchDB에서 트랜잭션과 잠금을 할 수 있습니까?


트랜잭션 (시작, 커밋 또는 롤백), 잠금 (업데이트 선택)을 수행해야합니다. 문서 모델 DB에서 어떻게 할 수 있습니까?

편집하다:

사례는 다음과 같습니다.

  • 경매 사이트를 운영하고 싶습니다.
  • 그리고 직접 구매하는 방법도 생각합니다.
  • 직접 구매에서는 품목 레코드의 수량 필드를 줄여야하지만 수량이 0보다 큰 경우에만 가능합니다. 이것이 내가 잠금과 트랜잭션이 필요한 이유입니다.
  • 잠금 및 / 또는 트랜잭션없이이 문제를 해결하는 방법을 모르겠습니다.

CouchDB로이 문제를 해결할 수 있습니까?


아니요. CouchDB는 "낙관적 동시성"모델을 사용합니다. 가장 간단한 용어로 이것은 업데이트와 함께 문서 버전을 보내고 현재 문서 버전이 보낸 것과 일치하지 않으면 CouchDB가 변경 사항을 거부한다는 것을 의미합니다.

정말 믿을 수 없을 정도로 간단합니다. CouchDB에 대한 많은 일반 트랜잭션 기반 시나리오를 재구성 할 수 있습니다. 하지만 CouchDB를 배울 때 RDBMS 도메인 지식을 버려야합니다. Couch를 SQL 기반 세계로 몰아 넣는 것보다 더 높은 수준에서 문제에 접근하는 것이 도움이됩니다.

재고 추적

설명하신 문제는 주로 재고 문제입니다. 항목을 설명하는 문서가 있고 "사용 가능한 수량"필드가 포함 된 경우 다음과 같은 동시성 문제를 처리 할 수 ​​있습니다.

  1. 문서를 검색하고 _revCouchDB가 보내는 속성을 기록해 둡니다.
  2. 수량 필드가 0보다 큰 경우 감소
  3. _rev속성을 사용하여 업데이트 된 문서를 다시 보내기
  4. (가) 경우 _rev현재 저장된 번호와 일치, 할!
  5. 충돌이있는 경우 ( _rev일치하지 않는 경우 ) 최신 문서 버전 검색

이 경우 생각할 수있는 두 가지 실패 시나리오가 있습니다. 최신 문서 버전의 수량이 0 인 경우 RDBMS에서와 마찬가지로 처리하고 사용자에게 구매하려는 것을 실제로 구매할 수 없음을 경고합니다. 최신 문서 버전의 수량이 0보다 큰 경우 업데이트 된 데이터로 작업을 반복하고 처음부터 다시 시작하면됩니다. 이로 인해 RDBMS보다 약간 더 많은 작업을 수행해야하며, 충돌하는 업데이트가 자주 발생하면 약간 짜증이 날 수 있습니다.

이제 제가 방금 제시 한 대답은 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


MrKurt의 답변을 확장합니다. 많은 시나리오의 경우 주식 티켓을 순서대로 교환 할 필요가 없습니다. 첫 번째 티켓을 선택하는 대신 나머지 티켓에서 무작위로 선택할 수 있습니다. 많은 수의 티켓과 많은 수의 동시 요청이 주어지면 첫 번째 티켓을 얻으려는 모든 사람에 비해 해당 티켓에 대한 경합이 훨씬 줄어 듭니다.


나머지 트랜잭션을위한 디자인 패턴은 시스템에 "장력"을 만드는 것입니다. 은행 계좌 거래의 인기있는 사용 사례의 경우 관련된 두 계정의 합계를 업데이트해야합니다.

  • "계정 11223에서 계정 88733로 USD 10 이전"거래 문서를 생성합니다. 이것은 시스템에 긴장을 만듭니다.
  • 모든 거래 문서에 대한 긴장 스캔을 해결하고
    • 소스 계정이 아직 업데이트되지 않은 경우 소스 계정을 업데이트합니다 (-10 USD)
    • 출처 계정이 업데이트되었지만 거래 문서에이를 표시하지 않는 경우 거래 문서를 업데이트합니다 (예 : 문서에서 "sourcedone"플래그 설정).
    • 대상 계정이 아직 업데이트되지 않은 경우 대상 계정을 업데이트합니다 (+10 USD)
    • 대상 계정이 갱신되었지만 거래 문서에이를 표시하지 않는 경우 거래 문서를 갱신하십시오.
    • 두 계정이 모두 업데이트 된 경우 거래 문서를 삭제하거나 감사를 위해 보관할 수 있습니다.

장력 스캔은 시스템의 장력 시간을 짧게 유지하기 위해 모든 "장력 문서"에 대한 백엔드 프로세스에서 수행되어야합니다. 위의 예에서 첫 번째 계정이 업데이트되었지만 두 번째 계정이 아직 업데이트되지 않은 경우 짧은 시간 동안 불일치가 예상됩니다. 이것은 Couchdb가 배포 된 경우 최종 일관성을 처리하는 것과 동일한 방식으로 고려해야합니다.

또 다른 가능한 구현은 트랜잭션의 필요성을 완전히 피하는 것입니다. 긴장 문서를 저장하고 관련된 모든 긴장 문서를 평가하여 시스템 상태를 평가하기 만하면됩니다. 위의 예에서 이것은 계정의 합계가이 계정이 관련된 거래 문서의 합계 값으로 만 결정된다는 것을 의미합니다. Couchdb에서는이를 맵 / 축소 뷰로 매우 멋지게 모델링 할 수 있습니다.


No, CouchDB is not generally suitable for transactional applications because it doesn't support atomic operations in a clustered/replicated environment.

CouchDB sacrificed transactional capability in favor of scalability. In order to have atomic operations you need a central coordination system, which limits your scalability.

If you can guarantee you only have one CouchDB instance or that everyone modifying a particular document connects to the same CouchDB instance then you could use the conflict detection system to create a sort of atomicity using methods described above but if you later scale up to a cluster or use a hosted service like Cloudant it will break down and you'll have to redo that part of the system.

So, my suggestion would be to use something other than CouchDB for your account balances, it will be much easier that way.


As a response to the OP's problem, Couch is probably not the best choice here. Using views is a great way to keep track of inventory, but clamping to 0 is more or less impossible. The problem being the race condition when you read the result of a view, decide you're ok to use a "hammer-1" item, and then write a doc to use it. The problem is that there's no atomic way to only write the doc to use the hammer if the result of the view is that there are > 0 hammer-1's. If 100 users all query the view at the same time and see 1 hammer-1, they can all write a doc to use a hammer 1, resulting in -99 hammer-1's. In practice, the race condition will be fairly small - really small if your DB is running localhost. But once you scale, and have an off site DB server or cluster, the problem will get much more noticeable. Regardless, it's unacceptable to have a race condition of that sort in a critical - money related system.

An update to MrKurt's response (it may just be dated, or he may have been unaware of some CouchDB features)

A view is a good way to handle things like balances / inventories in CouchDB.

You don't need to emit the docid and rev in a view. You get both of those for free when you retrieve view results. Emitting them - especially in a verbose format like a dictionary - will just grow your view unnecessarily large.

A simple view for tracking inventory balances should look more like this (also off the top of my head)

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

And the reduce function is even more simple

_sum

This uses a built in reduce function that just sums the values of all rows with matching keys.

In this view, any doc can have a member "InventoryChange" that maps product_key's to a change in the total inventory of them. ie.

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

Would add 10 hammer_1234's and 25 saw_4321's.

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

Would burn 5 hammers from the inventory.

With this model, you're never updating any data, only appending. This means there's no opportunity for update conflicts. All the transactional issues of updating data go away :)

Another nice thing about this model is that ANY document in the DB can both add and subtract items from the inventory. These documents can have all kinds of other data in them. You might have a "Shipment" document with a bunch of data about the date and time received, warehouse, receiving employee etc. and as long as that doc defines an InventoryChange, it'll update the inventory. As could a "Sale" doc, and a "DamagedItem" doc etc. Looking at each document, they read very clearly. And the view handles all the hard work.


Actually, you can in a way. Have a look at the HTTP Document API and scroll down to the heading "Modify Multiple Documents With a Single Request".

Basically you can create/update/delete a bunch of documents in a single post request to URI /{dbname}/_bulk_docs and they will either all succeed or all fail. The document does caution that this behaviour may change in the future, though.

EDIT: As predicted, from version 0.9 the bulk docs no longer works this way.


Just use SQlite kind of lightweight solution for transactions, and when the transaction is completed successfully replicate it, and mark it replicated in SQLite

SQLite table

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

You can also delete the transactions which are replicated successfully.

참고URL : https://stackoverflow.com/questions/299723/can-i-do-transactions-and-locks-in-couchdb

반응형