定義
Session.startTransaction(<options>)セッションに関連するマルチドキュメントトランザクションを開始します。 いつでも、1つのセッションに対して最大 1 つのトランザクションを開くことができます。
バージョン 4.2 での変更: MongoDB 4.2 以降では、シャーディングされたクラスターとレプリカセットの両方でマルチドキュメントトランザクションが利用できます。
重要
トランザクション内では、既存のコレクションに対する読み取りと書込み(CRUD)操作のみを指定できます。たとえば、マルチドキュメントトランザクションには、新しいコレクションの作成につながるような挿入操作を含めることはできません。
Session.startTransaction()メソッドは、次のオプションを持つドキュメントを取ることができます。{ readConcern: { level: <level>}, writeConcern: { w: <value>, j: <boolean>, wtimeout: <number> } } オプション説明readConcern任意。トランザクション内の全操作の読み取り保証 (read concern) を指定するドキュメントで、操作固有の読み取り保証 (read concern) に優先します。
次の読み取り保証 (read concern) レベルのいずれかを指定できます。
"local"と"majority"の読み取り保証 (read concern) については、MongoDB はより強力な読み取り保証 (read concern) を代用する場合があります。writeConcern任意。トランザクションに関する書込み保証 (write concern) を明記したドキュメントです。この書込み保証 (write concern) は、トランザクションのコミット操作と中止操作に適用されます。
トランザクション内の操作では
"w: 1"が使用され、操作固有の書込み保証 (write concern) は無視されます。"w: 1"書込み保証 (write concern) を使用してコミットすると、フェイルオーバー プロセス中にトランザクションがロールバックされる可能性があります。MongoDB Drivers の場合、トランザクションはデフォルトでクライアントレベルの書込み保証 (write concern) を使用します。
互換性
このメソッドは、次の環境でホストされている配置で使用できます。
MongoDB Atlas はクラウドでの MongoDB 配置のためのフルマネージド サービスです
MongoDB Enterprise: サブスクリプションベースの自己管理型 MongoDB バージョン
MongoDB Community: ソースが利用可能で、無料で使用できる自己管理型の MongoDB のバージョン
動作
トランザクション内でサポートされる操作
注意
アクセス制御を使用して実行中の場合は、トランザクション内の操作に対する特権が必要です。
マルチドキュメントトランザクションの場合は次のとおりです。
コレクションとインデックスはトランザクション内で作成できます。詳細については、「トランザクション内でのコレクションとインデックスの作成」を参照してください。
トランザクションで使用されるコレクションは、異なるデータベースにある可能性があります。
注意
クロスシャードの書き込みトランザクションでは新しいコレクションを作成できません。たとえば、あるシャードで既存コレクションに書き込み、別のシャードで暗示的にコレクションを作成する場合、MongoDB では同じトランザクションで両方の操作を実行できません。
Capped コレクションには書き込めません。
Capped コレクションから読み取る場合、読み取り保証(read concern)
"snapshot"は使用できません。(MongoDB 5.0 以降)configデータベース、adminデータベース、またはlocalデータベース内のコレクションの読み取りと書き込みはできません。system.*コレクションに書き込み (write) はできません。explainまたは類似コマンドを使用して、サポートされている操作のクエリプランを返すことはできません。
トランザクションの外部で作成されたカーソルの場合、トランザクション内で
getMoreを呼び出せません。トランザクション内で作成されたカーソルの場合、トランザクション外で
getMoreを呼び出せません。
killCursorsコマンドをトランザクションの最初の操作に指定することはできません。さらに、トランザクション内で
killCursorsコマンドを実行すると、サーバーは指定されたカーソルを直ちに停止します。トランザクションがコミットされるのを待ちません。
方式 | コマンド | 注意 |
|---|---|---|
シャーディングされていないコレクションで利用できます。 For sharded collections, use the aggregation pipeline with the
$group stage. See Distinct Operation. | ||
アップデート操作または置換操作が、存在しないコレクションに対して 詳細については、「 管理操作 」を参照してください。 | ||
存在しないコレクションに対して実行すると、コレクションが暗黙的に作成されます。 詳細については、「 管理操作 」を参照してください。 | ||
存在しないコレクションに対して実行すると、コレクションが暗黙的に作成されます。 詳細については、「 管理操作 」を参照してください。 | ||
存在しないコレクションに対して実行すると、コレクションが暗黙的に作成されます。 詳細については、「 管理操作 」を参照してください。 |
コレクションやインデックスの作成や削除など、データベース カタログに影響する操作は、マルチドキュメントトランザクションでは実行できません。たとえば、マルチドキュメントトランザクションには、新しいコレクションの作成につながるような挿入操作を含めることはできません。「制限付き操作」を参照してください。
情報提供コマンドには、hello、buildInfo、connectionStatus(およびこれらのヘルパー メソッド)などがあり、トランザクションに含めることができますが、最初の操作になることはできません。
読み込み設定 (read preference)
トランザクションは読み込み設定(read preference primaryをサポートします。
アトミック性
トランザクションが開いている間は、トランザクション内の操作によって行われたデータ変更は、トランザクションの外部からは参照できません。
トランザクションがコミットされると、トランザクションで行われたデータ変更はトランザクションの外部に保存、表示されます。つまり、トランザクションが他の変更をロールバックしながら、一部の変更をコミットすることはありません。
トランザクションがコミットされるまで、トランザクションで行われたデータ変更はトランザクションの外部には表示されません。
ただし、トランザクションが複数のシャードに書き込む場合、すべての外部読み取り操作が、コミットされたトランザクションの結果がシャード全体で表示されるまで待機する必要はありません。たとえば、トランザクションがコミットされ、書込み 1 がシャード A で表示されているものの、書込み 2 がシャード B にまだ表示されていない場合、読み取り保証(read concern)
"local"での外部読み取りは、書き込み 2 を見ることなく書き込み 1 の結果を読み取ることができます。トランザクションが中止されると、トランザクション内の書込み (write) によって行われたすべてのデータ変更は、参照可能にならずに破棄され、トランザクションは終了します。
例
hr データベース内の従業員のレコードに変更が加えられたときに、reporting データベース内の events コレクションを hr の変更と同期させるシナリオを考えてみましょう。つまり、これらの書込み (write) が単一のトランザクションとして実行され、両方の操作が成功するか失敗するかのいずれかになるようにする必要があります。
hr データベースの employees コレクションには次のドキュメントが含まれています:
{ "_id" : ObjectId("5af0776263426f87dd69319a"), "employee" : 3, "name" : { "title" : "Mr.", "name" : "Iba Ochs" }, "status" : "Active", "department" : "ABC" } { "_id" : ObjectId("5af0776263426f87dd693198"), "employee" : 1, "name" : { "title" : "Miss", "name" : "Ann Thrope" }, "status" : "Active", "department" : "ABC" } { "_id" : ObjectId("5af0776263426f87dd693199"), "employee" : 2, "name" : { "title" : "Mrs.", "name" : "Eppie Delta" }, "status" : "Active", "department" : "XYZ" }
reporting データベースの events コレクションには次のドキュメントが含まれています:
{ "_id" : ObjectId("5af07daa051d92f02462644a"), "employee" : 1, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "ABC", "old" : null } } { "_id" : ObjectId("5af07daa051d92f02462644b"), "employee" : 2, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "XYZ", "old" : null } } { "_id" : ObjectId("5af07daa051d92f02462644c"), "employee" : 3, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "ABC", "old" : null } }
次の例では、トランザクションを開き、employees ステータスの従業員のステータスを Inactive に更新し、対応するドキュメントを events コレクションに挿入し、2 つの操作を 1 つのトランザクションとしてコミットします。
// Runs the txnFunc and retries if TransientTransactionError encountered function runTransactionWithRetry(txnFunc, session) { while (true) { try { txnFunc(session); // performs transaction break; } catch (error) { // If transient error, retry the whole transaction if (error?.errorLabels?.includes("TransientTransactionError") ) { print("TransientTransactionError, retrying transaction ..."); continue; } else { throw error; } } } } // Retries commit if UnknownTransactionCommitResult encountered function commitWithRetry(session) { while (true) { try { session.commitTransaction(); // Uses write concern set at transaction start. print("Transaction committed."); break; } catch (error) { // Can retry commit if (error?.errorLabels?.includes("UnknownTransactionCommitResult") ) { print("UnknownTransactionCommitResult, retrying commit operation ..."); continue; } else { print("Error during commit ..."); throw error; } } } } // Updates two collections in a transactions function updateEmployeeInfo(session) { employeesCollection = session.getDatabase("hr").employees; eventsCollection = session.getDatabase("reporting").events; session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } ); try{ employeesCollection.updateOne( { employee: 3 }, { $set: { status: "Inactive" } } ); eventsCollection.insertOne( { employee: 3, status: { new: "Inactive", old: "Active" } } ); } catch (error) { print("Caught exception during transaction, aborting."); session.abortTransaction(); throw error; } commitWithRetry(session); } // Start a session. session = db.getMongo().startSession( { readPreference: { mode: "primary" } } ); try{ runTransactionWithRetry(updateEmployeeInfo, session); } catch (error) { // Do something with error } finally { session.endSession(); }