82

トランザクション (開始、コミット、またはロールバック)、ロック (更新の選択) を行う必要があります。ドキュメントモデルデータベースでそれを行うにはどうすればよいですか?

編集:

ケースはこれです:

  • オークションサイトを運営したい
  • 直接購入する方法もあると思います。
  • 直接購入では、アイテム レコードの数量フィールドを減らす必要がありますが、数量がゼロより大きい場合のみです。そのため、ロックとトランザクションが必要です。
  • ロックやトランザクションなしでそれに対処する方法がわかりません。

これを CouchDB で解決できますか?

4

7 に答える 7

149

いいえ。CouchDB は「楽観的並行性」モデルを使用します。簡単に言うと、これは、更新と共にドキュメント バージョンを送信し、現在のドキュメント バージョンが送信したものと一致しない場合、CouchDB が変更を拒否することを意味します。

実にシンプルです。CouchDB の多くの通常のトランザクション ベースのシナリオを再構築できます。ただし、CouchDB を学習するときは、RDBMS ドメインの知識を捨てる必要があります。Couch を SQL ベースの世界に合わせようとするのではなく、より高いレベルから問題にアプローチすることが役に立ちます。

在庫の追跡

あなたが概説した問題は、主に在庫の問題です。アイテムを説明するドキュメントがあり、「利用可能な数量」のフィールドが含まれている場合、次のような同時実行の問題を処理できます。

  1. ドキュメントを取得し、_revCouchDB が送信するプロパティをメモします。
  2. ゼロより大きい場合、数量フィールドを減らします
  3. _revプロパティを使用して、更新されたドキュメントを送り返します
  4. _revが現在保存されている番号と一致する場合は完了です。
  5. 競合がある場合 (_rev一致しない場合)、最新のドキュメント バージョンを取得する

この場合、考えられる 2 つの障害シナリオがあります。最新のドキュメント バージョンの数量が 0 の場合、RDBMS の場合と同じように処理し、購入したいものを実際に購入できないことをユーザーに警告します。最新のドキュメント バージョンの数量が 0 より大きい場合は、更新されたデータで操作を繰り返し、最初からやり直すだけです。これにより、RDBMS よりも少し多くの作業を行う必要があり、競合する更新が頻繁に行われる場合は少し面倒になる可能性があります。

さて、先ほどの回答は、CouchDB で RDBMS で行うのとほぼ同じ方法で作業を行うことを前提としています。私はこの問題に少し違ったアプローチをするかもしれません:

すべての記述子データ (名前、写真、説明、価格など) を含む「マスター製品」ドキュメントから始めます。次に、特定のインスタンスごとに と のフィールドを持つ「在庫チケット」ドキュメントを追加しproduct_keyますclaimed_by。ハンマーのモデルを販売していて、販売するモデルが 20 個ある場合、利用可能な各ハンマーを表す 、 などのhammer-1キーを含むドキュメントがあるかもしれません。hammer-2

次に、使用可能なハンマーのリストを表示するビューを作成し、reduce 関数を使用して「合計」を表示します。これらは完全に思いつきではありませんが、作業ビューがどのように見えるかについてのアイデアを提供するはずです。

地図

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

これにより、利用可能な「チケット」の一覧がプロダクト キー別に表示されます。誰かがハンマーを購入したいときにこれらのグループを取得し、取得に成功するまで ( and を使用しid_rev) 更新を送信することを繰り返すことができます (以前に取得したチケットは更新エラーになります)。

減らす

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

この reduce 関数は、請求されていないアイテムの総数を返すだけなinventory_ticketので、購入可能な「ハンマー」の数を知ることができます。

注意事項

この解決策は、提示された特定の問題について、合計約 3.5 分の思考に相当します。これを行うより良い方法があるかもしれません!とはいえ、競合する更新が大幅に減少し、新しい更新で競合に対応する必要性が削減されます。このモデルでは、複数のユーザーがプライマリ製品エントリのデータを変更しようとすることはありません。最悪の場合、複数のユーザーが 1 つのチケットを要求しようとすることになり、ビューからそれらのいくつかを取得した場合は、次のチケットに移動して再試行するだけです。

参照: https://wiki.apache.org/couchdb/Frequently_asked_questions#How_do_I_use_transactions_with_CouchDB.3F

于 2008-11-18T18:51:23.953 に答える
27

MrKurtの答えを拡張します。多くのシナリオでは、ストック チケットを順番に引き換える必要はありません。最初のチケットを選択する代わりに、残りのチケットからランダムに選択できます。多数のチケットと多数の同時リクエストがある場合、全員が最初のチケットを取得しようとする場合と比較して、これらのチケットの競合が大幅に減少します。

于 2008-11-25T10:02:12.037 に答える
22

レストフルトランザクションのデザインパターンは、システムに「テンション」を作成することです。銀行口座取引の一般的な使用例では、関連する両方の口座の合計を更新する必要があります。

  • 「アカウント11223からアカウント88733に10米ドルを送金する」トランザクションドキュメントを作成します。これにより、システムに緊張が生じます。
  • すべてのトランザクションドキュメントのテンションスキャンを解決し、
    • ソースアカウントがまだ更新されていない場合は、ソースアカウントを更新します(-10米ドル)
    • ソースアカウントが更新されたが、トランザクションドキュメントにこれが表示されない場合は、トランザクションドキュメントを更新します(たとえば、ドキュメントにフラグ "sourcedone"を設定します)
    • ターゲットアカウントがまだ更新されていない場合は、ターゲットアカウントを更新します(+10 USD)
    • ターゲットアカウントが更新されたが、トランザクションドキュメントにこれが表示されない場合は、トランザクションドキュメントを更新します
    • 両方のアカウントが更新されている場合は、トランザクションドキュメントを削除するか、監査のために保持することができます。

張力のスキャンは、システムの張力の時間を短くするために、すべての「張力ドキュメント」のバックエンドプロセスで実行する必要があります。上記の例では、最初のアカウントが更新されたが、2番目のアカウントがまだ更新されていない場合、短時間の不整合が予想されます。これは、Couchdbが配布されている場合に結果整合性を処理するのと同じ方法で考慮する必要があります。

別の可能な実装では、トランザクションの必要性を完全に回避します。張力ドキュメントを保存し、関連するすべての張力ドキュメントを評価することによってシステムの状態を評価するだけです。上記の例では、これは、アカウントの合計が、このアカウントが関係するトランザクションドキュメントの合計値としてのみ決定されることを意味します。Couchdbでは、これをマップ/リデュースビューとして非常にうまくモデル化できます。

于 2009-07-31T23:01:59.983 に答える
6

いいえ、CouchDB は通常、トランザクション アプリケーションには適していません。クラスタ化/複製された環境でのアトミック操作をサポートしていないためです。

CouchDB は、スケーラビリティを優先してトランザクション機能を犠牲にしました。アトミック操作を行うには、スケーラビリティを制限する中央調整システムが必要です。

CouchDB インスタンスが 1 つしかないこと、または特定のドキュメントを変更する全員が同じ CouchDB インスタンスに接続することを保証できる場合は、競合検出システムを使用して、上記の方法を使用して一種の原子性を作成できますが、後でクラスターにスケールアップする場合または、Cloudant のようなホストされたサービスを使用すると、機能しなくなり、システムのその部分をやり直す必要があります。

したがって、アカウントの残高には CouchDB 以外のものを使用することをお勧めします。その方がはるかに簡単です。

于 2011-10-25T03:28:31.207 に答える
6

OPの問題への対応として、Couchはおそらくここでの最良の選択ではありません. ビューを使用することは在庫を追跡する優れた方法ですが、0 に固定することは多かれ少なかれ不可能です。問題は、ビューの結果を読んで「hammer-1」アイテムを使用してもよいと判断し、それを使用するドキュメントを作成するときの競合状態です。問題は、ビューの結果が 0 以上のhammer-1 がある場合にのみ、hammer を使用するようにドキュメントを作成するアトミックな方法がないことです。100 人のユーザー全員が同時にビューにクエリを実行し、1 つの Hammer-1 が表示された場合、全員が Hammer 1 を使用するドキュメントを作成できるため、-99 の Hamm-1 が得られます。実際には、競合状態はかなり小さくなります。DB が localhost を実行している場合は非常に小さくなります。しかし、規模を拡大し、オフサイトの DB サーバーまたはクラスターを使用すると、問題はさらに顕著になります。

MrKurt の応答の更新 (単に日付が変わっているか、CouchDB の機能の一部を知らなかった可能性があります)

ビューは、CouchDB で残高や在庫などを処理するのに適した方法です。

ビューでdocidとrevを発行する必要はありません。ビューの結果を取得すると、両方とも無料で取得できます。特に辞書のような冗長な形式でそれらを発行すると、ビューが不必要に大きくなるだけです。

在庫残高を追跡するための単純なビューは、次のようになるはずです (これも私の頭から離れています)。

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

reduce 関数はさらにシンプルです。

_sum

これは、一致するキーを持つすべての行の値を合計する組み込みの reduce 関数を使用します。

このビューでは、すべてのドキュメントに、product_key をそれらの総在庫の変化にマップする "InventoryChange" メンバーを含めることができます。すなわち。

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

10個のhammer_1234と25個のsaw_4321を追加します。

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

インベントリから 5 つのハンマーを燃やします。

このモデルでは、データを更新することはなく、追加するだけです。これは、更新の競合が発生する可能性がないことを意味します。データ更新のすべてのトランザクションの問題がなくなります:)

このモデルのもう 1 つの優れた点は、DB 内のすべてのドキュメントで、在庫からアイテムを追加および削除できることです。これらのドキュメントには、他のあらゆる種類のデータを含めることができます。受領日時、倉庫、受領従業員などに関する一連のデータを含む「出荷」ドキュメントがあり、そのドキュメントが InventoryChange を定義している限り、在庫が更新されます。「Sale」ドキュメントや「DamagedItem」ドキュメントなども同様です。各ドキュメントを見ると、非常に明確に読み取れます。そして、ビューはすべてのハードワークを処理します。

于 2013-08-09T16:17:06.343 に答える
2

実際には、ある意味でできます。HTTPドキュメントAPIを確認し、「1つのリクエストで複数のドキュメントを変更する」という見出しまでスクロールします。

基本的に、 URI / {dbname} / _bulk_docsへの1回のPOSTリクエストで一連のドキュメントを作成/更新/削除でき、それらはすべて成功するか、すべて失敗します。ただし、このドキュメントでは、この動作が将来変更される可能性があることを警告しています。

編集:予測どおり、バージョン0.9以降、バルクドキュメントはこのように機能しなくなりました。

于 2009-02-20T08:09:54.137 に答える