web-dev-qa-db-ja.com

mongodbドキュメントをロックすることはできません。必要な場合はどうなりますか?

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

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

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

私はリポジトリパターンを使用して、mongodbコレクションへのアクセスを抽象化しているので、私はCRUDオペレーションしか持っていません。

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

ありがとう

31
Mathieu Pagé

Mongodbのアップグレード作業中にこの質問に遭遇しました。この質問が尋ねられたときとは異なり、mongodbはドキュメントレベルのロックアウトをサポートします。

差出人: http://docs.mongodb.org/manual/faq/concurrency/

「MongoDBのロックはどのくらい細かいですか?

バージョン3.0で変更されました。

バージョン3.0以降、MongoDBにはWiredTigerストレージエンジンが同梱されており、ほとんどの読み取りおよび書き込み操作でオプティミスティック同時実行制御を使用しています。 WiredTigerは、グローバルレベル、データベースレベル、およびコレクションレベルでインテントロックのみを使用します。ストレージエンジンが2つの操作間の競合を検出すると、書き込みの競合が発生し、MongoDBがその操作を透過的に再試行します。」

7
Mahesh

ステータスパラメータを追加し、操作 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つのドキュメントしか変更できないことです。

それが役に立てば幸い。

13
golja

「医師、私がやると痛いthis "

「それではいけないdoそれだ!」

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

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

6
Charlie Martin

スレッドセーフにするための古典的な解決策は、ロック(ミューテックス)を使用することです。これは、楽観的ロックとは対照的に、悲観的ロックとも呼ばれます- ここ

悲観的ロックがより効率的なシナリオがあります(詳細 ここ )。実装もはるかに簡単です(楽観的ロックの主な困難は衝突からの回復です)。

MongoDBはロックのメカニズムを提供していません。しかし、これはアプリケーションレベル(つまり、コード内)で簡単に実装できます。

  1. ロックを取得
  2. ドキュメントを読む
  3. ドキュメントを変更
  4. 文書を書く
  5. ロックを解除する

ロックの細分性は、グローバル、コレクション固有、レコード/ドキュメント固有など、異なる場合があります。ロックが具体的であるほど、パフォーマンスの低下は少なくなります。

3

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

楽観的同時実行制御 を使用する必要があると思います。

すべてのドキュメントにタイムスタンプ、ハッシュ、または別の一意の識別子(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();
}
3
Mathieu Pagé

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

例:

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

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

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

2
Prashant Bhate

pdate: MongoDB 3.2.2がWiredTiger Storage実装をデフォルトエンジンとして使用している場合、MongoDBはドキュメントレベルでデフォルトのロックを使用します。バージョン3.0で導入されましたが、バージョン3.2.2でデフォルトになりました。したがって、MongoDBには現在ドキュメントレベルのロックがあります。

2
Satyam

配列内の要素の順序が重要ではない場合、 $ Push 演算子は、スレッドが互いの変更を上書きしないように十分安全でなければなりません。

0

サーバーが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()を使用するだけです

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

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

0
Azee

4.0以降、MongoDBはレプリカセットに対して Transactions をサポートしています。分割クラスターのサポートは、MongoDB 4.2で提供される予定です。トランザクションを使用すると、書き込みの競合が発生した場合、DBの更新のコミットが中止され、問題が解決します。

トランザクションはパフォーマンスの点ではるかにコストがかかるため、トランザクションを貧弱なNoSQLスキーマ設計の言い訳として使用しないでください。

0
Govind Rai

別の質問で質問を書く代わりに、私はこれに答えようとします:このWiredTigerストレージが私がここで指摘した問題を処理するかどうか疑問に思います: mongodbへの挿入を制限する

0
oderfla