fbpx

よくある質問

MongoDB FAQ #3: 並列処理

この部分はバージョン3.0で変更されました

MongoDBでは同じデータを複数のクライアントが読込み・書込みすることができます。MongoDB では、ロックやその他の並列性管理の手法(concurrency control)を利用し、複数のクライアントが同時に同一のデータを変更することを防止して一貫性を保っています。これらのメカニズムにより、1つのドキュメントへの全書込みは、完了するか失敗するかのどちらかに帰結し、クライアントが矛盾を含んだデータビューを目にすることはありません。

1. MongoDB はどのようなタイプのロックを使用しますか?

MongoDBは複数粒度のロック[1] を提供します。データベースレベル、コレクションレベル、ドキュメントレベルの粒度のロックがあり、コレクション以下ではストレージエンジンによる独自の並列処理機構が存在します。(例: WiredTigerストレージエンジンではドキュメントレベルの並列処理機構を提供します)

MongoDBはクライアントに対する読込み・書込みロックを使用し、データベースやコレクションなどのリソースへのクライアントの同時読込みを可能にします。しかしながら、MMAPv1ストレージエンジンでは排他操作により同時に1つの書込み操作しか許容されません。

読み取り操作のための共有ロック(S)モードおよび書込み操作のための排他的ロック(X)モードに加えて、インテント共有(IS)およびインテント排他 (IX)モードは、より細かな粒度のロックを使用してリソースを読込み/書込みします。特定の粒度でロックすると、すべての上位レベルがインテントロックを使用してロックされます。

例えば、(モードXを使用して)書込みのためにあるコレクションをロックする場合、対応するデータベースロックとグローバルロックの両方をインテント排他(IX)モードでロックする必要があります。単一のデータベースは、ISおよびIXモードで同時にロック可能ですが、排他ロック(X)はその他のモードと共存することはできません。また、共有ロック(S)はインテント共有ロック(IS)とのみ共存可能です。

全てのロックは優先度を持たず公平に処理され、読取り/書込み操作は、順番にキュー(待ち行列)に入れられ、順番に処理されます。しかしながら、スループットを最適化するために、1つのリクエストが許可されると、その他のすべての互換性のあるリクエストが同時に許可され、競合するリクエストをリリースする可能性があります。例えば、ロック(X)がリリースされたばかりで、競合するキューには次の項目が含まれていると考えてください:

IS → IS → X → X → S → IS

 厳密な先入れ先出し(FIFO) の 順序では、最初の2つのISモードのみが許可されます。しかし、MongoDBでは全てのISおよびSモードを許可し、一旦全てが流れると、その間に仮に新しいISもしくはSリクエストがキューに入ったとしても、Xが許可されます。許可の付与は、常にキューの中のすべての他のリクエストを先に進めるため、リクエストがなくなるような状況にはなりません。

db.serverStatus() および db.currentOp() のアウトプットでは、ロックのモードは次のように表されます:

ロックモード 詳細
R 共有ロック(S)を表します
W 排他ロック(X)を表します
r インテント共有ロック(IS)を表します
w インテント排他ロック(IX)を表します

[1] 詳細は、Wikipediaの "Multiple granularity locking(複数粒度ロック)" をご参照ください。

2. MongoDB のロックの粒度はどのレベルですか?

この部分はバージョン3.0で変更されました

WiredTiger 向け

MongoDB Ver 3.0から、MongoDBはWiredTigerストレージエンジンを採用しています。

WiredTiger はほとんどの読込み/書込み操作で、楽観的並行性制御(Optimistic concurrency control)を使用します。WiredTigerは、グローバルレベル、データベースレベル、コレクションレベルでインテントロックのみ使用します。ストレージエンジンが2つの操作間で矛盾を検知すると、そのうち1つが書込み矛盾をもたらすため、MongoDB は内部でその操作をリトライします。

いくつかのグローバルな操作、典型的には短い時間で複数のデータベースを変更する操作には、インスタンス全体のロックが必要となります。他の操作、例えばコレクションをドロップする等の操作は排他DBロックが必要です。

MMAPv1 向け

MMAPv1 ストレージエンジンは、3.0リリースシリーズからコレクションレベルのロックを使用します。これ以前のバージョンのデータベースロックが最も粒度の小さいロックであったものからの改善です。サードパーティーストレージエンジンはコレクションレベルのロックを使用するか、もしくは自前のより緻密な粒度の並列管理を適用します。

例えば、あなたがMMAPv1 ストレージエンジンを使用したデータベースに6個のコレクションを保持していたとします。そしてある操作でコレクションレベルの書込みロックが発生すると、他の5個のコレクションは読込み・書込み操作のできる状態です。排他データベースロックでは、操作がロックを保持している間ずっと6個すべてのコレクションを操作不可の状態にします。

3. mongodインスタンスのロック状態を確認するにはどうすればよいでしょうか?

ロックの活用情報のリポートには次の方法のいずれかを使用してください。

特にserverStatusメソッドの戻り値locksドキュメント、もしくは操作の出力locksフィールドはmongodインスタンスのロック競合の量とロックの型についての情報を提供します。

db.serverStatus() および db.currentOp() のアウトプットでは、ロックのモードは次のように表されます:

ロックモード 詳細   
R 共有ロック(S)を表します
W 排他ロック(X)を表します
r インテント共有ロック(IS)を表します
w インテント排他ロック(IX)を表します

操作を終わらせるには、 db.killOp() を使います。

4. 書込みもしくは読込み操作がロックを引き起こすことがありますか?

いくつかの条件下では、書込み・読込み操作がロックを引き起こすことがあります。

長時間実行される書込み・読込み操作、例えば、クエリ、更新、削除は多くの条件下で(ロックを)引き起こします。MongoDBの操作、例えば複数パラメータを使用したupdate()のような複数ドキュメントに対する書込み操作は個別のドキュメントの変更に対してロックを引き起こします。

WiredTigerのようにドキュメントレベルの並列性管理(concurrency control)をサポートしているストレージエンジンは(対象のドキュメントのみをロックすればよいので)、グローバル、データベース、コレクションレベルのインテントロックを発生させる必要がありません。これにより他の読込み、書込みがブロックされなくなります。ただし操作の結果として以下のような理由により周期的なロックが発生します。

  • 長期的なストレージトランザクションを防ぐため。これはメモリ内で大量のデータを保持する可能性があるためです。
  • インタラプションポイントとして機能する。これにより長時間稼働している操作を強制終了できます。
  • インデックス/コレクションのドロップおよび作成といったコレクションへの排他的アクセスを必要とする操作を許可するため。

MongoDB のMMAPv1ストレージエンジンはアクセスパターンを元にヒューリスティックな予測を用いて、データが物理メモリに入っているか、読込み実施前に予測します。MongoDB が、データは物理メモリに入っていないと予想すると、MongoDB がデータをメモリにロードする間、操作がロックを引き起こします。メモリにデータが格納されると、操作を完了するために再度ロックを必要とします。

5. 一般的なクライアント操作に使用されるロックはどのようなものですか?

次の表は、代表的な操作とその操作がドキュメントレベルロックのストレージエンジンで使用するロックタイプを示すものです。

操作 データベース コレクション
クエリの発行(Issue a query) r(インテント共有ロック) r(インテント共有ロック)
データの挿入(Insert) w(インテント排他ロック) w(インテント排他ロック)
データの削除(Remove) w(インテント排他ロック) w(インテント排他ロック)
データの更新(Update) w(インテント排他ロック) w(インテント排他ロック)
集計操作(Aggregation) r(インテント共有ロック) r(インテント共有ロック)
インデックス作成(フォアグラウンド) W(排他ロック) --
インデックス作成(バックグラウンド) w(インテント排他ロック) w(インテント排他ロック)
コレクションのリスト化 R(共有ロック) --
Map-Reduce W(排他ロック)およびR(共有ロック) w(インテント排他ロック) および r(インテント共有ロック)

6. データベースをロックする管理者コマンドは何ですか?

管理者コマンドのいくつかは、データベースを通常より長いあいだ排他ロックできます。より大きなデータベースでは、クライアントへの影響を避けるために mongod インスタンスをオフラインにすることも考慮したほうがよいでしょう。例: mongod がレプリカセットの一部である場合、メンテナンスが進行中は mongod をオフラインにしてサービスの他のメンバーをロード可能にします。

次に挙げる管理者操作では、より長いあいだのデータベースレベルでの排他ロックが必要です。

コマンド 手法
cloneCollectionAsCapped --
compact --
convertToCapped --
copydb. この操作は全てのデータベースをロックします。「MongoDBにおける操作は1つ以上のデータベースをロックしますか?」を参照してください db.copyDatabase(). この操作は全てのデータベースをロックします。「MongoDBにおける操作は1つ以上のデータベースをロックしますか?」を参照してください
非常に大きな(数GB以上)キャップドコレクションを作成するときのcreate 非常に大きな(数GB以上)キャップドコレクションを作成するときにdb.createCollection()を呼び出します
backgroundオプションがtrueにセットされていない状態で実行されたcreateIndexes backgroundオプションがtrueにセットされていない状態でdb.collection.createIndex() および db.collection.createIndexes()を呼び出します
reIndex db.collection.reIndex()
repairDatabase db.repairDatabase()

次に挙げる管理者操作はデータベースをロックしますが、極めて短い間のみです。

コマンド 手法
authenticate db.auth()
createUser db.createUser()
dropIndexes db.collection.dropIndex()
getLastError db.getLastError()
isMaster db.isMaster()
replSetGetStatus rs.status()
renameCollection db.collection.renameCollection()
serverStatus db.serverStatus()

こちらもご参照ください:MongoDBにおける操作は1つ以上のデータベースをロックしますか?

7. MongoDB 操作が2つ以上のデータベースをロックすることはありますか?

次に挙げるMongoDB操作は複数のデータベースをロックします。

  • db.copyDatabase() は一度に mongod インスタンス全体をロックします。
  • db.repairDatabase() はグローバル書込みロックを獲得し、db.repairDatabase()が終わるまで他の操作をブロックします。
  • User authentication は2.6 user credentialsを使用している場合、admin データベースの読込みロックが必要です。2.4スキーマを使用している環境では、ユーザクレデンシャル、認証の際にアクセスしようとしているデータベースに加えてadminデータベースの読込みロックを引き起こします
  • レプリカセットのプライマリーへの全ての書込みは、書込みを受けるデータベースおよびローカルデータベースを短時間ロックします。ローカルデータベースへの書込みはmongodにプライマリoplogへの書込みを発生させ、わずかに時間を要します
  • レプリカセットの状態遷移(state transitions)はグローバルな排他ロックが必要です。

8. シャーディングは並列性にどのように影響しますか?

シャーディング は、複数のmongodインスタンス上にコレクションを分散させることで並列性を改善します。シャードサーバー(例えばmongosプロセス)によって 様々なダウンストリームmongod インスタンスに多くの操作が実施できます。

シャードされたクラスターではロックはクラスター全体ではなく個々のシャードに適用されます。つまり各mongodインスタンスはシャードされたクラスター内の他のインスタンスから独立したものであり、それぞれに適用されているロックを使用します。1つのmongodインスタンスへの操作は、他のインスタンスの操作をブロックしません。

9. 並列性はレプリカセットプライマリーにどのように影響しますか?

レプリカセットと共にMongoDBがプライマリーにコレクションを書き込む際、MongoDBはローカルデータベースの特別なコレクションであるプライマリーoplogも書込みます。したがってMongoDBはコレクションのデータベースとローカルデータベースの両方をロックしなくてはいけません。データベースの一貫性を保ち、レプリケーション構成であっても書込み操作が「all or nothing」な操作であるために、mongodはそれら(コレクションおよびローカル)のデータベースを同時にロックしなければなりません。

レプリカセットに書き込む際、ロックのスコープはプライマリに対して適用されます。

10. 並列性はセカンダリーレプリカにどのように影響しますか?

レプリケーション構成では、MongoDBはセカンダリーレプリカへの書き込みが必ずしも直列的に行われるわけではありません。セカンダリーレプリカは oplogエントリーをバッチで収集し、それらのバッチを並列に適用します。セカンダリーは書込み操作を適用している間は読み込みを許可しません。また、書込み操作はoplog に現れる順番で適用されます。

11. MongoDBはトランザクションをサポートしていますか?

ドキュメントは関連データを共に動かすことができます。関連データがもしそのように動かされない場合は、関連スキーマの個々の親子テーブルを横断してモデルされます。 したがって、MongoDB のアトミックなシングルドキュメントオペレーションは、それ単体で、大多数のアプリケーションのデータ整合性ニーズに応えるトランザクション・セマンティックス を供給しています。1つの操作で、1つもしくはそれ以上のフィールドが書込まれます。これには複数のサブ・ドキュメントおよび配列の個別の要素の更新も含まれます。ドキュメントの更新に関する完全な保障がMongoDBによって提供されています。(ドキュメントの更新時に)エラーが発生すると、更新前の状態にロールバックするため、クライアントはドキュメントに関する一貫性のあるビューが得られることが保証されています。

MongoDB 4.0で予定されているマルチドキュメントトランザクションについては、開発者が「RDBで慣れ親しんだトランザクションだ」とお感じになるでしょう。マルチ・ステートメントである点、シンタックスが似ている点、アプリへの追加が容易である点、等。トランザクションはスナップショット隔離によって、グローバルで常に安定したデータビューを提供し、all-or-nothing 実行を可能にします。またそれはトランザクションを必要としないワークロードのパフォーマンスに影響を与えません。マルチドキュメントトランザクションの追加によって、開発者によるMongoDBの適用がさらに容易になります。beta program はこちら

注:

製品の機能の開発・リリース・および時期についての説明の一切の裁量は米国MongoDB, Inc.に属します。ここに記載している情報は製品の大まかな方向性を要約したものであり、購入の決定に使用されるべきものではありません。また本情報はマテリアル、コード、機能を提供するコミットメント、確約、法的責任を生じさせるものでもありません。

12. MongoDB はどのような分離分離レベルを保証していますか?

MongoDBは並列読込み・書込みに関して次の保証を提供しています。MMAPv1もしくはWiredTigerストレージエンジンいずれの場合もこれらの保証が行われます。

  1. シングルドキュメントに関する書込み操作はアトミックです。つまり、書込みがドキュメントの複数のフィールドを更新している間に、別の読込み命令が同じドキュメントを読み込んだ場合、一部のフィールドのみが更新されたドキュメントを読み込むことはありません(つまり全く更新されていないドキュメントか、全てのフィールドが更新されたドキュメントのいずれかを受け取ることになります)。スタンドアロンのmongod インスタンスでは、シングルドキュメントへの読込み・書込み操作のセットはシリアライズ可能です。ロールバックがない場合のみシングルドキュメントへの読込み・書込み操作のセットはシリアライズ可能です。
  2. クエリ述部の正確性について db.collection.find() は一致するドキュメントのみを返し、 db.collection.update() は一致するドキュメントにのみ書込みます。
  3. 並べ替えの正確性について (例えばdb.collection.find() あるいは db.collection.aggregate()のような) 並べ替え順序を要求する読み取り操作の場合、並べ替え順序が同時書込みによって妨げられることはありません。

MongoDBでは単一ドキュメントに対してアトミックな操作であることを保証していますが、読み込み・書込み操作は実行中に様々なドキュメントにアクセスします。複数ドキュメント操作はトランザクションとしては実行されず、(個別に分離された複数ドキュメントの)並列書込みとなります。これは、MMAPv1エンジンとWiredTigerストレージエンジンの両方に対して、通常のシステム操作では、次のような動作が予想されます。

  1. Non-point-in-time 読込み操作:読込み操作が「t1」時刻に開始され、ドキュメント読込みを開始したとします。そのあと「t2」時刻に、書込み操作がドキュメント更新をコミットしたとします。読みこみ命令の結果、更新されたバージョンのドキュメントを見る場合があります。ドキュメントのPoint-in-timeスナップショットを見ることはありません。
  2. シリアライズされない操作: 読込み操作が、「t1」時刻に「d1」ドキュメントを読込んだとします。その後「t3」時刻に書込み操作が「d1」を更新したとします。これは読込みと書込みの順番に依存性があります。もし操作がシリアライズされているのであれば書込み操作の前に読込み操作が実行されなければいけません。しかし、書込み操作が「t2」時刻に「d2」ドキュメントを更新し、その後、読込み操作が「t4」時刻に「d2」ドキュメントを読込むことも仮定してみましょう。この場合、書込み操作後に読取り操作となるシリアル化されたスケジュールが必要な、書込み/読込み依存性が発生します。このような依存性サイクルがあるため、直列化は不可能です。
  3. (読込み操作は、)読込み操作中に、読込み中に更新され読込み条件に合致したドキュメントを見逃す可能性があります

"Atomicity and Transactions"をあわせて参照ください。

13. 読込みでディスクにコミットされていない変更を閲覧することができますか?

この部分はバージョン3.2で変更されました。 MongoDB 3.2では readConcern オプションが導入されました。majority readConcern を使用しているクライアントは、書込みの結果が durable になるまではそれらを見ることができません。

"local" readConcern を使用しているreader は、write concern level やジャーナル設定に関わらず、書込み結果がdurable になる前にそれらを見ることができます。その結果、アプリケーションは次のような動作になるかもしれません。

  1. MongoDBは、書込みを行ったクライアントが結果を通知される前に、並列読込みを行うクライアントは書込み結果を反映した結果を読み込むことを許容します。書込みが認識されるタイミングは、書込みconcern level によって異なります。詳しくはこちら(Write Concern)をご覧ください。
  2. 読込み命令は、レプリカセットのフェイルオーバーや、ノードの電源断などによってロールバックされたデータを読み出す場合があります。これは、部分的に書き込まれた、つまり一貫性のない状態にあるドキュメントを読込み操作で読込むことができるという意味ではありません。ドキュメントの操作は常にアトミックであり、必ず更新前もしくは更新後のドキュメントが得られます。1つのドキュメント内のフィールドが部分的に更新されたドキュメントが読み込まれることはありません。

他のシステムでは、これらのセマンティックスをread uncommitted と呼んでいます。

この部分は3.2バージョンで変更されました