原子性とトランザクション
項目一覧
MongoDBでは、書込み操作は複数の値を変更する場合でも、単一のドキュメントのレベルで アトミックです。複数の更新コマンドが並行して実行される場合、個々のコマンドはクエリ条件が引き続き一致することを確認します。
同時更新コマンドが互いに競合しないことを保証するには、更新フィルターのフィールドの予想される現在の値を指定できます。
例
このドキュメントを含むコレクションについて考えてみます。
db.games.insertOne( { _id: 1, score: 80 } )
これらの更新操作は同時に発生します。
// Update A db.games.updateOne( { score: 80 }, { $set: { score: 90 } } ) // Update B db.games.updateOne( { score: 80 }, { $set: { score: 100 } } )
1 回のアップデート操作により、ドキュメントの score
フィールドが 90
または 100
のいずれかに設定されます。この更新が完了すると、2 番目の更新操作はクエリ述語 { score: 80 }
と一致しなくなり、実行されません。
警告
同時更新操作の場合、更新されていないフィールドにフィルターを指定すると、予期しない結果を引き起こすことがあります。例、次の更新操作が同時に発生する場合を考えてみましょう。
// Update A db.games.updateOne( { _id: 1 }, { $set: { score: 90 } } ) // Update B db.games.updateOne( { _id: 1 }, { $set: { score: 100 } } )
1 つの更新操作が完了しても、残りの操作は引き続きクエリ述語 { _id: 1 }
と一致します。その結果、両方の更新操作が実行され、保存された score
値は 2 番目の更新操作を反映します。これは問題です。最初の更新を発行したクライアントは、更新が上書きされ、score
の値が予想と異なることを示す指示を受信せず、 の値が予想と異なるためです。
更新フィルターが更新対象とは異なるフィールドにある場合に書き込み操作の競合を防ぐには、$inc
演算子を使用します。
例、次の更新操作が同時に発生する場合を考えてみましょう。
// Update A db.games.updateOne( { _id: 1 }, { $inc: { score: 10 } } ) // Update B db.games.updateOne( { _id: 1 }, { $inc: { score: 20 } } )
1 つの更新操作が完了しても、残りの操作は引き続きクエリ述語 { _id: 1 }
と一致します。ただし、これらの操作は score
の現在の値を変更するため、相互に上書きすることはありません。両方の更新が反映され、結果の score
は 110
になります。
Tip
Store Unique Values
フィールドの一意の値のみを含めるようにするには、ユニークインデックスを作成します。一意なインデックス により、挿入や更新によって重複データが作成されるのを防ぎます。複数のフィールドにユニークインデックスを作成して、フィールド値の組み合わせを確実に一意にすることができます。例については、「 一意なインデックスの作成 」を参照してください。
マルチドキュメントトランザクション
単一の書込み操作(例: db.collection.updateMany()
)は複数のドキュメントを変更します。各ドキュメントの変更は不可分ですが、操作全体は不可分ではありません。
複数のドキュメントへの書込み操作を実行する場合、1 回の書込み操作でも複数の書込み操作でも、他の操作がインターリーブすることがあります。
MongoDB は(単一または複数のコレクション内の)複数のドキュメントへの読み取りと書込みにアトミック性を必要とする状況で、レプリカセットやシャーディングされたクラスターでのトランザクションを含む分散トランザクションをサポートします。
詳細についてはトランザクションを参照してください。
重要
ほとんどの場合、分散トランザクションでは 1 つのドキュメントの書き込み (write) よりもパフォーマンス コストが高くなります。分散トランザクションの可用性は、効果的なスキーマ設計の代わりにはなりません。多くのシナリオにおいて、非正規化されたデータモデル(埋め込みドキュメントと配列)が引き続きデータやユースケースに最適です。つまり、多くのシナリオにおいて、データを適切にモデリングすることで、分散トランザクションの必要性を最小限に抑えることができます。
トランザクションの使用に関するその他の考慮事項(ランタイム制限や oplog サイズ制限など)については、「本番環境での考慮事項」も参照してください。