43

単一の mongodb ドキュメントをロックできないことはわかっています。実際、コレクションをロックする方法もありません。

ただし、このシナリオでは、複数のスレッド (またはプロセス、重要ではありません) がドキュメントを変更するのを防ぐ方法が必要だと思います。これが私のシナリオです。

タイプ A のオブジェクトを含むコレクションがあります。タイプ A のドキュメントを取得し、ドキュメントのプロパティである要素を配列に追加し ( a.arr.add(new Thing())、ドキュメントを mongodb に保存するコードがあります。このコードは並列であり、私のアプリケーションの複数のスレッドがこれらの操作を実行できます。今のところ、スレッドが同じドキュメントに対してこれらの操作を並行して実行するのを防ぐ方法はありません。スレッドの 1 つが他のスレッドの作業を上書きする可能性があるため、これは良くありません。

私はリポジトリ パターンを使用して mongodb コレクションへのアクセスを抽象化しているため、私の処分では CRUD 操作しかありません。

考えてみると、問題を引き起こしているのはmongodbの制限ではなく、リポジトリパターンの制限である可能性があります。とにかく、どうすればこのコードを「スレッドセーフ」にできますか? この問題にはよく知られた解決策があると思いますが、mongodb とリポジトリ パターンは初めてなので、すぐにはわかりません。

ありがとう

4

13 に答える 13

22

私が今考えている唯一の方法は、ステータスパラメータを追加し、操作findAndModify()を使用することです。これにより、ドキュメントをアトミックに変更できます。少し遅いですが、うまくいくはずです。

ステータス属性を追加し、ドキュメントを取得するときにステータスを「IDLE」から「PROCESSING」に変更するとします。次に、ドキュメントを更新し、ステータスを再び「IDLE」に更新してコレクションに保存します。

コード例:

var doc = db.runCommand({
              "findAndModify" : "COLLECTION_NAME",
              "query" : {"_id": "ID_DOCUMENT", "status" : "IDLE"},
              "update" : {"$set" : {"status" : "RUNNING"} }
}).value

COLLECTION_NAME と ID_DOCUMENT を適切な値に変更します。デフォルトでは、findAndModify() は古い値を返します。これは、ステータス値がクライアント側で IDLE のままであることを意味します。したがって、更新が完了したら、すべてを再度保存/更新します。

一度に 1 つのドキュメントしか変更できないことに注意してください。

それが役に立てば幸い。

于 2012-06-18T06:20:14.140 に答える
7

「博士、これをすると痛い

「じゃあ、そんなことしない!」

基本的に、あなたが説明していることは、そこにシリアル依存関係があるように聞こえます-MongoDBか何か、あなたのアルゴリズムには、操作をシリアル化する必要があるポイントがあります。これは固有のボトルネックになります。どうしても実行する必要がある場合は、それを保護するために何らかのセマフォを配置する必要があります。

したがって、見る場所はあなたのアルゴリズムです。あなたはそれを排除できますか?たとえば、「レコードをローカルに取得」の更新など、ある種の競合解決で処理して、ストア後に新しいレコードがそのキーで取得されるようにすることはできますか?

于 2012-06-18T02:18:00.313 に答える
4

インターネットで調査中に解決策を見つけたので、自分の質問に答えます。

私がする必要があるのは、 Optimistic Concurency Controlを使用することだと思います。

タイムスタンプ、ハッシュ、または別の一意の識別子 (ここでは UUID を使用します) をすべてのドキュメントに追加します。ドキュメントが変更されるたびに、一意の識別子を変更する必要があります。ドキュメントを更新する前に、次のようなことを行います(疑似コードで):

var oldUUID = doc.uuid;
doc.uuid = new UUID();
BeginTransaction();
if (GetDocUUIDFromDatabase(doc.id) == oldUUID)
{
   SaveToDatabase(doc);
   Commit();
}
else
{
   // Document was modified in the DB since we read it. We can't save our changes.
   RollBack();
   throw new ConcurencyException();
}
于 2012-06-18T15:25:11.637 に答える
2

別の方法は、インプレース更新を行うことです

例:

http://www.mongodb.org/display/DOCS/Updating#comment-41821928

db.users.update( { level: "Sourcerer" }, { '$push' : { 'inventory' : 'magic wand'} }, false, true );

これにより、「魔法の杖」がすべての「Sourcerer」ユーザーのインベントリ配列にプッシュされます。各ドキュメント/ユーザーの更新はアトミックです。

于 2012-10-07T09:07:40.247 に答える
2

サーバーが 1 つを超えるシステムを使用している場合は、分散ロックが必要になります。

私はHazelcastを使用することを好みます。

保存中に、エンティティ ID で Hazelcast ロックを取得し、データを取得して更新し、ロックを解放できます。

例: https://github.com/azee/template-api/blob/master/template-rest/src/main/java/com/mycompany/template/scheduler/SchedulerJob.java

lock.lock()代わりに使用するだけですlock.tryLock()

ここでは、Spring コンテキストで Hazelcast を構成する方法を確認できます。

https://github.com/azee/template-api/blob/master/template-rest/src/main/resources/webContext.xml

于 2014-04-03T15:17:03.460 に答える
-1

MongoDB のアトミック オペレーターを使用したいようですね: http://www.mongodb.org/display/DOCS/Atomic+Operations

于 2012-10-30T07:50:35.683 に答える