17

アプリケーションをAppEngineデータストアからMongoDBバックエンドに移植している最中であり、「ドキュメントの更新」の一貫性について質問があります。1つのドキュメントの更新がすべてアトミックで分離されていることは理解していますが、異なるレプリカセット間で「一貫性がある」ことを保証する方法はありますか?

私たちのアプリケーションでは、多くのユーザーが1回の更新中にいくつかの埋め込みドキュメント(オブジェクト)を挿入することで、1つのドキュメントを同時に更新しようとする可能性があります。これらの更新がすべてのレプリカで論理的に一貫した方法で行われるようにする必要があります。つまり、1人のユーザーがいくつかの埋め込みドキュメントを親ドキュメントに「配置」すると、他のユーザーは、確実に完了するまで、埋め込みドキュメントを親ドキュメントに配置できなくなります。最初のユーザーの更新を読んで受け取りました。

つまり、一貫性とは、2人のユーザーが1つのドキュメントに対してまったく同時に更新を実行しようとした場合に、MongoDBがそれらの更新の一方のみを許可し、もう一方を破棄する(または少なくとも両方が発生するのを防ぎます)。ここでは、標準の「シャーディング」ソリューションを使用できません。これは、単一の更新が単なるインクリメントまたはデクリメント以上のもので構成されているためです。

1つの特定のドキュメントの一貫性を保証する最良の方法は何ですか?

4

2 に答える 2

19

これを実現する方法は他にもありますが、1つの方法は、ドキュメントをバージョン管理し、ユーザーが以前に読んだバージョンに対してのみ更新を発行することです(つまり、最後に読んだ後で他のユーザーがドキュメントを更新していないことを確認します)。pymongoを使用したこの手法の簡単な例を次に示します。

>>> db.foo.save({'_id': 'a', 'version': 1, 'things': []}, safe=True)
'a'
>>> db.foo.update({'_id': 'a', 'version': 1}, {'$push': {'things': 'thing1'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': True, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 1}

上記のキー「n」は1であり、ドキュメントが更新されたことを示します。

>>> db.foo.update({'_id': 'a', 'version': 1}, {'$push': {'things': 'thing2'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': False, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 0}

ここでは、間違ったバージョンに対して更新しようとしましたが、キー「n」は0です。

>>> db.foo.update({'_id': 'a', 'version': 2}, {'$push': {'things': 'thing2'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': True, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 1}
>>> db.foo.find_one()
{'things': ['thing1', 'thing2'], '_id': 'a', 'version': 3}

この手法は安全な書き込みの使用に依存していることに注意してください。そうしないと、更新されたドキュメントの数を示す確認応答が得られません。これのバリエーションでは、findAndModifyコマンドを使用します。このコマンドは、ドキュメントを返すか、None(Pythonの場合)クエリに一致するドキュメントが見つからなかった場合に返します。findAndModifyドキュメントの新しいバージョン(つまり、更新が適用された後)または古いバージョンのいずれかを返すことができます。

于 2011-11-10T15:34:46.857 に答える
3

MongoDBは、マスターマスターレプリケーションまたはマルチバージョン同時実行を提供しません。つまり、書き込みは常にレプリカセット内の同じサーバーに送信されます。デフォルトでは、セカンダリからの読み取りも無効になっているため、デフォルトの動作では、一度に1つのサーバーとのみ通信します。したがって、アトミック修飾子(など)を使用する場合、セーフモードでの一貫性のない結果について心配する必要はありません$inc, $push

これらのアトミック修飾子に制限したくない場合は、dcrosta(およびmongo docs)で推奨されているようにコンペアアンドスワップを行うことをお勧めします。これはすべて、レプリカセットやシャーディングとは関係ありませんが、単一サーバーのシナリオでも同じです。

データベース/ノードに障害が発生した場合にも読み取りの一貫性を確保する必要がある場合は、セーフモードで大多数のサーバーに書き込みを行っていることを確認する必要があります。

安全でない読み取りを許可した場合、 2つのアプローチは異なる動作をします。アトミック更新操作は引き続き機能しますが(ただし、予期しない結果が生じる可能性があります)、コンペアアンドスワップアプローチは失敗します。

于 2011-11-11T12:59:32.763 に答える