2

最後に結果を見る


ドキュメントDBを使用したい(さまざまな理由で)-おそらくCouchDBまたはMongoDB。ただし、複数のドキュメントのトランザクションにもACIDが必要です。

ただし、「追加のみ」のモデルで作業する予定です。変更は新しいドキュメントとして追加されます(追加は追加、更新はコピー+変換データの追加、削除は同じID +削除フラグを持つ空のドキュメントの追加)。定期的に、データベースで圧縮を実行して、最新でないドキュメントを削除します。

それを念頭に置いて、次のアイデアに穴はありますか?

進行中の現在のトランザクションのコレクションを維持します。このコレクションは、進行中のトランザクションのトランザクションID(GUID +タイムスタンプ)を持つドキュメントを保持します。

Atomicity:
    On a transaction:
        Add a document to the transactions in progress collection.
        Add the new documents (add is add, update is copy+add, delete is add with ID and “deleted” flag).
            Each added document will have the following management fields:
            Transaction ID.
            Previous document ID (linked list).
        Remove the document added to the transactions in progress collection.
    On transaction fail:
        Remove all added documents
        Remove the document from the transactions in progress collection.
    Periodically:
        Go over all transaction in progress, get ones that have been abandoned (>10 minutes?), remove the associated documents in the DB (index on transaction ID) and then remove the transaction in progress.
Read transaction consistency (read only committed transactions):
    On data retrieval:
        Load transactions in progress set.
        Load needed documents.
        For all documents, if the document transaction ID is in “transactions in progress” or later (using timestamp), load the previous document in the linked list (recursive).

MVCCに少し似ていて、Gitに少し似ています。開始する前になんとか終了したことがわかっているトランザクションによって、取得コンテキストを設定しました。「トランザクションのリビジョン」ではなく「進行中のトランザクション」のリストを保持することで、単一のシーケンス(したがって単一の実行)を回避します。そしてもちろん、私はコミットされていないトランザクションを読むことを避け、競合のロールバックを提供します。

だから-これに穴はありますか?私のパフォーマンスはひどく損なわれますか?

編集1:お願いします-「複数のドキュメントトランザクションが必要な場合は、ドキュメントデータベースを使用しないでください」を槌で打たないでください。とにかく他の理由でドキュメントデータベースが必要です。

Edit2:取得トランザクションの開始後に開始されるトランザクションからのデータを回避するために、タイムスタンプが追加されました。タイムスタンプをシーケンスIDに変更する可能性があります。

Edit3:これが私が考えた別のアルゴリズムです-それは上記のものよりも良いかもしれません:

新しいアルゴリズム-理解しやすい(そして今回は修正できる可能性があります:))

Support structures:
transaction_support_tempalte {
    _created-by-transaction: <txid>
    _made-obsolete-by-transaction: <txid>
}

transaction_record { //
    transaction_id: <txid>
    timestamp: <tx timestamp>
    updated_documents: {
        [doc1_id, doc2_id...]
    }   
}

transaction_numer { //atomic counter - used for ordering transactions.
    _id: "transaction_number"
    next_transaction_id: 0 //initial.
}

Note: all IDs are model object IDs, not DB ids (don't confuse with logical IDs which are different).
DB ID - different for each document - but multiple DB documents are revisions of one model object.
Model object ID - same for all revisions of the model object.
Logical ID - client-facing ID.


First time setup:
1. Create the transaction_number document:

Commit process:
1. Get new transaction ID by atomic increment on the transaction number counter.
2. Insert a new transaction record with the transaction id, the timestamp and the updated documents.
3. Create the new version for each document. Make sure the _created-by-transaction is set.
4. Update the old version of each updated or deleted document as 
   "_made-obsolete-by-transaction" with the transaction id.
   This is the time to detect conflicts! if seen a conflict, rollback.
   Note - this can be done as find-and-modify rather then by serializing the entire document again.
5. Remove the transaction record.

Cleanup process:
1. Go over transaction record, sorted by id, ascending (oldest transaction first).
2. For each transaction, if it expired (by timestamp), do rollback(txid).

Rollback(txid) process:
1. Get the transaction record for the given transaction id.
2. For each document id in the "updated documents":
    2.1 If the document exists and has "_made-obsolete-by-transaction" with 
        the correct transaction id, remove the _made-obsolete-by-transaction data.
3. For each document with the _created-by-transaction-id:
    3.1 remove the document.
4. Remove the transaction record document.

Retrieval process:
1. Top-transaction-id = transaction ID counter.
2. Read all transactions from the transactions collection. 
   Current-transaction-ids[] = Get all transaction IDs.
3. Retrieve documents as needed. Always use "sort by transaction_id, desc" as last sort clause.
    3.1 If a document "_created-by-transaction-id" is in the Current-transaction-ids[] 
        or is >= Top-transaction-id - ignore it (not yet committed).
    3.2 If a document "_made-obsolete-by-transaction" is not in the Current-transaction-ids[] 
        and is < Top-transaction-id - ignore it (a newer version was committed).
4. We may have to retrieve more chunks to satisfy original requests if documents were ignored.

開始時にドキュメントはコミットされましたか?
現在実行中のトランザクション(取得を開始する前に開始されたが、その時点ではまだコミットされていないトランザクション)にトランザクションIDを持つドキュメントが表示された場合、それは望ましくありません。トランザクションID>=最上位のトランザクションID(取得を開始した後に開始されたトランザクション)のドキュメントが表示された場合、それは望ましくありません。

ドキュメントは最新(最新バージョン)ですか?
現在のトランザクションID(開始前に開始されたトランザクション)になく、最上位のトランザクションID(開始後に開始されたトランザクション)である、廃止されたドキュメントが表示された場合、過去にコミットを終了したトランザクションがありました。このドキュメントを廃止しました-したがって、私たちはそれを望んでいません。

ソートが損なわれないのはなぜですか?
並べ替えを最後の句として追加するため、実際の並べ替え作業が常に最初に表示されます。実際の並べ替えの「バケット」ごとに、異なるバージョンのモデルオブジェクトを表す複数のドキュメントを取得する場合があります。ただし、モデルオブジェクト間の並べ替え順序は変わりません。

カウンターがトランザクションをシリアルに(一度に1つずつ)実行しないのはなぜですか?
これはRDBMSではないため、実際にはトランザクションがないため、「更新の選択」の場合のようにトランザクションがコミットされるのを待ちません。別のトランザクションは、それが完了するとすぐにアトミックな変更を行うことができます。

圧縮:時々
、圧縮を行う必要があります。本当に古いドキュメントをすべて取得して、別のデータストアに削除します。これは、実行中の取得またはトランザクションには影響しません。

最適化:

  1. 条件をクエリ自体に入れます。
  2. すべてのインデックスにトランザクションIDを追加します。
  3. 同じモデルオブジェクトIDを持つドキュメントが異なるノードにシャーディングされないようにしてください。

費用はいくらですか?
とにかく履歴と監査に複数のドキュメントバージョンが必要だとすると、追加のコストは、カウンターをアトミックに更新し、トランザクションレコードを作成し、各モデルオブジェクトの以前のバージョンを「封印」し(廃止マーク)、トランザクションドキュメントを削除することです。これは大きすぎてはいけません。上記の仮定が有効でない場合、特に検索の場合、追加コストが非常に高くなることに注意してください。


結果:

上記のアルゴリズムを実装しました(マイナーな変更を加えた改訂版)。機能的には、機能しています。ただし、パフォーマンス(少なくとも、マスタースレーブレプリケーショントポロジに3つのノードがあるMongoDBを超える場合、fsyncは必要ありませんが、「コミット」が終了する前にレプリケーションが必要です)はひどいものです。書いたばかりのものをさまざまなスレッドから常に読んでいます。トランザクションコレクションで一定のコレクションロックが発生し、インデックスが一定のロールオーバーに対応できません。10個のフィーダースレッドを使用する小さなトランザクションのパフォーマンスは、20TPSに制限されています。

要するに、良い汎用ソリューションではありません。

4

1 に答える 1

2

計画の詳細に立ち入ることなく、mongoDBによるACID要件のサポートを最初に検討することが役立つかもしれないと思いました。

Atomicity:Mongoは、個々のドキュメントのアトミックな変更をサポートしています。通常、最も重要なアトミック操作は「$set」とfindAndModifyです。これらの操作と一般的なmongoDBのアトミック性に関するドキュメントは次のとおりです。

http://www.mongodb.org/display/DOCS/Atomic+Operations
[http://www.mongodb.org/display/DOCS/Updating#Updating-%24set][1]
http://www.mongodb.org/display/DOCS/findAndModify+Command

一貫性:達成するのが難しく、非常に複雑です。この投稿で要約しようとはしませんが、このテーマに関する一連の素晴らしい投稿があります。

http://blog.mongodb.org/post/475279604/on-distributed-consistency-part-1
[http://blog.mongodb.org/post/498145601/on-distributed-consistency-part-2-some-eventual][2]

分離:mongoDBの分離はドキュメントには存在しますが、それ以上のレベルには存在しません。繰り返しますが、これは複雑な問題です。上記のAtomicOperationsリンクに加えて、私が見つけた最高のリソースは、次のスタックオーバーフロースレッドです。

MongoDBがfsync()を使用しないのはなぜですか?(耐久性に関する情報の一部は古くなっていますが、一番上の答えは、一般的にこの主題のちょっとした金鉱です)

耐久性:ユーザーがデータの耐久性を確保する主な方法は、getLastErrorコマンド(詳細については以下のリンクを参照)を使用して、呼び出しが戻る前にレプリカセット内のノードの大部分がデータを書き込んだことを確認することです。

http://www.mongodb.org/display/DOCS/getLastError+Command#getLastErrorCommand-majority 
http://docs.mongodb.org/manual/core/replication-internals/ (linked to in the above document)

mongoのACIDについてこれをすべて知っていると、mongoですでに解決されている同様の問題の例をいくつか調べると非常に役立ちます。私が期待する次の2つのリンクは、非常に完全で主題に沿っているため、非常に役立ちます。

Two-Phase Commits: http://cookbook.mongodb.org/patterns/perform-two-phase-commits/

Transactions for e-commerce work: http://www.slideshare.net/spf13/mongodb-ecommerce-and-transactions-10524960

最後に、私は尋ねなければなりません:なぜあなたは取引をしたいのですか?mongoDBのユーザーが、目標を達成するために本当にACIDが必要であることに気付くことはめったにありません。先に進み、トランザクションを取得するためだけにmongoの上にレイヤー全体を実装する前に、一歩下がって別の観点から問題にアプローチすることを試みる価値があるかもしれません。

于 2012-09-13T20:49:51.197 に答える