質問

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

編集:

ケースはこれです:

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

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

役に立ちましたか?

解決

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

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

在庫の追跡

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

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

この場合、考えられる障害シナリオは2つあります。最新のドキュメントバージョンの数量が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;
}

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

警告

このソリューションは、あなたが提示した特定の問題に対する約3.5分間の思考を表します。これを行うより良い方法があるかもしれません!とはいえ、競合する更新プログラムを大幅に削減し、新しい更新プログラムとの競合に対応する必要性を削減します。このモデルでは、複数のユーザーがプライマリ製品エントリのデータを変更しようとすることはありません。最悪の場合、複数のユーザーが単一のチケットを要求しようとし、viからそれらのいくつかを取得した場合

他のヒント

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

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

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

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

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

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

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

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

だから、私の提案は、アカウントの残高にCouchDB以外のものを使用することです。その方がずっと簡単です。

OPの問題への対応として、ここではおそらくCouchが最良の選択ではありません。ビューを使用することは在庫を追跡するための優れた方法ですが、0に固定することは多かれ少なかれ不可能です。問題は、ビューの結果を読み取る際の競合状態であり、「ハンマー-1」を使用しても問題ないと判断します。アイテムを選択し、それを使用するドキュメントを作成します。問題は、ビューの結果が>である場合にハンマーを使用するドキュメントを書くだけのアトミックな方法がないことです。 0ハンマー-1。 100人のユーザーがすべて同時にビューを照会し、ハンマー1を1つ見ると、ハンマー1を使用するドキュメントをすべて作成でき、その結果、ハンマー1が-99になります。実際には、競合状態はかなり小さくなります-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

これは、すべての行の値を単に一致するキー。

このビューでは、どのドキュメントにもメンバー「InventoryChange」を含めることができます。 product_keyをそれらの総在庫の変化にマッピングします。すなわち。

{
    "_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を定義している限り、在庫を更新します。 「セール」ができるようにdoc、および" DamagedItem" docなど。各ドキュメントを見ると、非常にはっきりと読んでいます。そして、ビューはすべてのハードワークを処理します。

実際には、ある意味では可能です。 HTTPドキュメントAPI を見て、「"複数のドキュメントを変更」という見出しまでスクロールします。単一のリクエスト"。

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

編集:予測どおり、バージョン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