あなたがstackoverflowで答えを信じるなら、すべてが悪いです。
MongoDB doesn't support complex multi-document transactions. If that is something you absolutely need it probably isn't a great fit for you.
If transactions are required, perhaps NoSQL is not for you. Time to go back to ACID relational databases.
ただし、MVCCに基づくトランザクション(ACID * )を信じて実装することはありません。 以下は、これらのトランザクションがどのように機能するかについての物語であり、コードを見たいと思う人のために、 GitHub (慎重に、java)にようこそ。MongoDB does a lot of things well, but transactions is not one of those things.
投稿はMongoDBに関するものではなく、compare-and-setを使用してトランザクションを作成する方法であり、ストレージが提供する範囲で永続性が正確に提供されます。
データモデル
他の多くのNoSQLソリューションとは異なり、MongoDBは比較と設定をサポートしています。 ACIDトランザクションを追加できるのはCASサポートです。 CASをサポートする他のNoSQLストレージ(HBase、Project Voldemort、ZooKeeperなど)を使用する場合、ここで説明したアプローチを適用できます。
CASとは何ですか?
, , . - , , .
実際、トランザクションで変更するすべてのオブジェクトはCASで保護する必要があります。これはデータモデルに影響します。 銀行の仕事をシミュレートすると仮定します。以下は保護ありとなしの口座モデルです。これから、残りを変更する方法が明確であることを望みます。
無防備 | 被告 | |
モデル | | |
データ変更 | | |
さらに、オブジェクトにはバージョンがあり、オブジェクトへの変更はそのバージョンを考慮するという事実に焦点を当てませんが、オブジェクトへの変更は競合アクセスで失敗する可能性があることを覚えておく必要があります。
実際、バージョンの追加は、トランザクションをサポートするためにモデルに加える必要があるすべての変更ではありません。完全に変更されたモデルは次のようになります。
{ _id : ObjectId(".."), version : 0, value : { name : "gov", balance : 600 }, updated : null, tx : null }
追加されたフィールド-更新および送信。 これは、トランザクションプロセスで使用されるオーバーヘッドです。 更新の構造は値と同じです。つまり、これはオブジェクトの修正バージョンであり、トランザクションが成功すると値に変わります。 txはObjectIdクラスのオブジェクトです-トランザクションを表すオブジェクトの_idの外部キー。 トランザクションを表すオブジェクトもCASによって保護されています。
アルゴリズム
アルゴリズムを説明するのは簡単で、その正確さが明白で、より複雑になるように説明することです。 したがって、定義する前にいくつかのエンティティを操作する必要があります。
以下は、アルゴリズムが後で構成される元のステートメント、定義、およびプロパティです。
- 値には常に過去のある時点で真であった状態が含まれます
- 読み取り操作はデータベース内のデータを変更できます
- 読み取り操作はべき等です
- オブジェクトは次の3つの状態になります。クリーン-c、ダーティアンコミット-d、ダーティアンコミット-dc
- トランザクションでは、状態のオブジェクトのみが変更されます:c
- 状態間の可能な遷移:c→d、d→c、d→dc、dc→c
- トランザクション開始の遷移:c→d、d→dc、dc→c
- 読み取り時に可能な遷移:d→c
- 遷移d→cがあった場合、内部で遷移c→dがあったトランザクションはコミット時に落ちます
- データベースを操作する際の操作がすべて失敗する
- 倒れた読み取り操作を繰り返す必要があります
- 倒れたレコードでは、新しいトランザクションを開始する必要があります
- コミットが失敗した場合、成功したかどうかを確認する必要があります。そうでない場合は、もう一度トランザクションを繰り返します。
- トランザクションを表すオブジェクト(_id = tx)が削除された場合に渡されるトランザクション
州
クリーン状態は、トランザクションが成功した後のオブジェクトを表します。値にはデータが含まれ、更新され、txはnullです。
ダーティな非セキュア状態は、トランザクションの時点でのオブジェクトを表し、更新には新しいバージョンが含まれ、txはトランザクションを表すオブジェクトの_idであり、このオブジェクトは存在します。
ダーティコミット状態は、トランザクションが成功した後のオブジェクトを表しますが、それ自体がクリーンアップされる前に落ち、更新にはトランザクションを表すオブジェクトのtx-_idが含まれますが、オブジェクト自体は既に削除されています。
取引
- トランザクションに参加するオブジェクトを読み取ります
- トランザクションを表すオブジェクトを作成します(tx)
- 更新された各オブジェクトに新しい値を書き込み、tx-tx._idに書き込みます
- TXオブジェクトを削除します
- 各オブジェクトの値に、更新された値、txおよび更新されたnullifyを書き込みます
読書
- オブジェクトを読む
- それがきれいな場合-それを返します。
- 汚れている場合、更新された値、tx、更新されたゼロの値を書き込みます
- 汚れたものがそうでない場合-txのバージョンを変更し、更新されたtxを無効にします
- ステップ1に進みます
正しいことを確信していない人のために、宿題はすべてのプロパティとステートメントが満たされていることを確認し、それらを使用してACIDを証明することです
おわりに
MongoDBにトランザクションを追加しました。 しかし、実際には、これは万能薬ではなく、制限があります。一部は以下にリストされ、一部はコメントに記載されています
- リポジトリからレコードが成功したという確認を受け取った場合、レコードは実際に成功し、このデータは失われないという仮定の下で、すべてがうまく機能します(データベースは一貫しており、トランザクションは失われません)
- トランザクションは楽観的であるため、異なるフローから高頻度でオブジェクトを変更する場合、トランザクションを使用しない方が良い
- 1つのトランザクションでn個のオブジェクトを変更するために2n + 2クエリが使用されます
- 時間が経つにつれて、ドロップされたトランザクションからtxオブジェクトを蓄積します-定期的に古いものを削除する必要があります
よくある質問
このようなトランザクションは、シャーディングやログの無効化にどのように役立ちますか?
サーバーエラーが発生した場合、実際にはデータベースの一貫性のない状態を取得できますが、記録時にクライアントがクラッシュすることによって引き起こされる一貫性のない状態から保護されています。 2番目のリスクが1番目のリスクよりも大きい場合、トランザクションを使用すると、システムの信頼性が向上します。
単一ノード構成でmongaを使用していますが、トランザクションは役立ちますか?
はい、ジャーナリングを使用すると、正直なACIDトランザクションを取得できます。 使用しない場合、信頼性を高めるための2番目の方法であるレプリケーションを使用していないため、データ損失の可能性にすでに同意しています。 同意すると、通常モードのトランザクションは競合アクセスおよびクライアントエラーとの一貫性を維持しますが、サーバーがクラッシュした場合、それを失う可能性があります。 しかし、これはそれほど怖いものではありません。単一のノードが落ちた場合、システムが利用できなくなるため、ノードを再起動する前にリカバリ手順をより困難にし、一貫性を復元できます。
公式ドキュメントの 2フェーズトランザクションを使用しないのはなぜですか?
彼らは複数のクライアントで動作しないためです。 より正確に機能しますが、大きな制限があります。
- 1つの要素に対するすべての変更は可換的である必要があります(等化はもはや適用されません)
- 各スレッドにはIDが必要です。これらのスレッドは決して死なず、互いに通信できる必要があります。
そうしないと、一貫性と可用性が失われます(凍結されたトランザクションは可能です-唯一の合理的なステップは、これらのトランザクションに参加するオブジェクトの読み取り/書き込みを拒否することです)。