本番環境での考慮事項
項目一覧
以下のページでは、トランザクションを実行中の運用環境の注意点をいくつか示します。 これらは、トランザクションをレプリカセットで実行するか、シャーディングされたクラスターで実行するかにかかわらず当てはまります。 シャーディングされたクラスターでトランザクションを実行中の場合、シャーディングされたクラスターに固有のその他の考慮事項については、 「運用環境に関する考慮事項(シャーディングされたクラスター) 」も参照してください。
可用性
MongoDB はレプリカセットでのマルチドキュメントトランザクションをサポートします。
分散トランザクションは、シャーディングされたクラスターでのマルチドキュメントトランザクションのサポートを追加し、レプリカセットでのマルチドキュメントトランザクションの既存のサポートを組み込んでいます。
注意
分散トランザクションとマルチドキュメントトランザクション
この 2 つのタームは同義語です。分散トランザクションとは、シャーディングされたクラスターとレプリカセットでのマルチドキュメントトランザクションを指します。(シャーディングされたクラスターかレプリカセットかを問わず)マルチドキュメントトランザクションは、分散トランザクションとも呼ばれます。
機能の互換性
トランザクションを使用するには、配置のすべてのノードの FeatureCompatibilityVersion が次のバージョン以上である必要があります。
配置 | 最小 featureCompatibilityVersion |
---|---|
レプリカセット | 4.0 |
シャーディングされたクラスター | 4.2 |
ノードの機能の互換性バージョンを確認するには、該当ノードに接続して次のコマンドを実行します。
db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
詳細については、setFeatureCompatibilityVersion
に関する参考ページを参照してください。
実行時間制限
注意
MongoDB Atlas で最大トランザクション有効期間を設定するには、Atlas ドキュメントの 「トランザクション有効期間の設定」 を参照してください。
デフォルトでは、トランザクションの実行時間は 1 分未満である必要があります。この制限は、transactionLifetimeLimitSeconds
を使用して mongod
インスタンスに対して変更できます。シャーディングされたクラスターの場合、シャーディングされたレプリカセットの全ノードのパラメーターを変更する必要があります。この制限を超えるトランザクションは期限切れと見なされ、定期的なクリーンアップ プロセスによって中止されます。
シャーディングされたクラスターの場合は、commitTransaction
に maxTimeMS
制限を指定することもできます。詳細については、「シャーディングされたクラスターのトランザクションの時間制限」を参照してください。
oplog のサイズ制限
MongoDB は、トランザクション内のすべての書込み (write) 操作に対して 1 つのエントリを作成するのではなく、トランザクション内のすべての書込み (write) 操作をカプセル化するために必要な数の oplog エントリを作成します。これにより、すべての書込み (write) 操作に対して、1 つの oplog エントリによって課せられるトランザクションの合計サイズ 16 MB の制限が削除されます。合計サイズの制限はなくなりましたが、各 oplog エントリは BSON ドキュメント サイズ制限の 16MB 以内である必要があります。
WiredTiger キャッシュ
ストレージ キャッシュの負荷がパフォーマンスに悪影響を与えないようにするには、次のようにします。
トランザクションを放棄する場合は、トランザクションを中止します。
トランザクションの個々の操作中にエラーが発生した場合は、トランザクションを中止して再試行してください。
transactionLifetimeLimitSeconds
は、ストレージ キャッシュの負荷を軽減するために、期限切れのトランザクションが定期的に中止されるようにします。
注意
コミットされていないトランザクションが WiredTiger キャッシュに過度の負荷をかける場合、トランザクションは中止され、書込み競合 (write conflict) エラーが返されます。
トランザクションが大きすぎて WiredTiger キャッシュに収まらない場合、トランザクションは中止され、TransactionTooLargeForCache
エラーが返されます。
トランザクションとセキュリティ
アクセス制御を使用して実行中、トランザクション内の操作に対する権限が必要です。
監査付きで実行している場合、中止されたトランザクションの操作は引き続き監査されます。ただし、トランザクションが中止されたことを示す監査イベントはありません。
シャード構成の制限
シャーディングされたクラスターのうち writeConcernMajorityJournalDefault
が false
に設定されているもの(インメモリ ストレージエンジンを使用する投票ノードのあるシャードなど)ではトランザクションを実行できません。
シャーディングされたクラスターとアービタ
レプリカセットにアービタがある場合、トランザクションを使用してシャードキーを変更することはできません。 アービタは、 マルチシャード トランザクションに必要なデータ操作に参加することはできません。
書込み操作が複数のシャードにまたがるトランザクションで、アービタを含むシャードを対象に読み取りまたは書込み操作が実行される場合、そのトランザクションはエラーとなり中止します。
ロックの取得
デフォルトでは、トランザクションの操作に必要なロックが取得されるまで、トランザクションは最大 5
ミリ秒待機します。トランザクションが 5
ミリ秒以内に必要なロックを取得できない場合、トランザクションは中止されます。
トランザクションによりは、すべてのロックはアボート時やコミット時に解放されます。
Tip
トランザクションを開始する直前にコレクションを作成または削除する際に、トランザクション内のコレクションにアクセスする場合、書込み保証(write concern)付きで "majority"
に作成または削除操作を発行して、トランザクションが必要なロックを取得できるようにします。
ロック リクエスト タイムアウト
注意
MongoDB Atlas クラスターでは、 setParameter
コマンドの使用が制限されています。 詳細については、Atlas ドキュメントの「 Atlas でサポートされていないコマンド」を参照してください。
Atlas クラスターのパラメータを変更するには、 Atlas サポート にお問い合わせください。
maxTransactionLockRequestTimeoutMillis
パラメーターを使用して、トランザクションがロックを取得するまでの待機時間を調整できます。maxTransactionLockRequestTimeoutMillis
を増やすと、トランザクション内の操作は必要なロックを取得するために指定された時間待機できるようになります。これにより、高速に実行中のメタデータ操作など、一時的な同時ロック取得でトランザクションが中止されるのを防ぐことができます。ただし、これにより、デッドロックしたトランザクション操作の中止が遅れる可能性があります。
maxTransactionLockRequestTimeoutMillis
を -1
に設定することで、操作固有のタイムアウトを使用することもできます。
保留中の DDL 操作とトランザクション
マルチドキュメントトランザクションが進行中の場合、同じデータベースまたはコレクションに影響する新しい DDL 操作はトランザクションの背後で待機します。これらの保留中の DDL 操作が存在している間、保留中の DDL 操作と同じデータベースまたはコレクションにアクセスする新しいトランザクションは、必要なロックを取得できず、maxTransactionLockRequestTimeoutMillis
の時間待機した後に中止されます。さらに、同じデータベースまたはコレクションにアクセスする新しい非トランザクション操作は、maxTimeMS
制限に達するまでブロックされます。
次のシナリオを検討してみましょう。
- コレクションのロックが必要な DDL 操作
進行中のトランザクションが
hr
データベースのemployees
コレクションに対してさまざまな CRUD 操作を実行している間に、管理者がemployees
コレクションに対してdb.collection.createIndex()
DDL 操作を発行します。createIndex()
には、コレクションに対して排他コレクション ロックが必要です。進行中のトランザクションが完了するまで、
createIndex()
操作はロックを取得するために待機する必要があります。employees
コレクションに影響し、createIndex()
が保留中に開始される新しいトランザクションは、createIndex()
が完了するまで待機する必要があります。保留中の
createIndex()
DDL 操作は、hr
データベース内の他のコレクションのトランザクションには影響しません。たとえば、hr
データベースのcontractors
コレクションに対する新しいトランザクションは、通常どおり開始して完了できます。- データベース ロックが必要な DDL 操作
進行中のトランザクションが
hr
データベースのemployees
コレクションに対してさまざまな CRUD 操作を実行している間に、管理者がrenameCollection
DDL 操作を発行して、vendors.contractors
コレクションの名前をhr.contractors
に変更します。renameCollection
では、ソース データベース(vendors
)と異なる場合、ターゲット データベース(hr
)のデータベース ロックが必要です。進行中のトランザクションが完了するまで、
renameCollection
操作はロックを取得するために待機する必要があります。hr
データベースまたはそのコレクションのいずれかに影響し、renameCollection
が保留中に開始される新しいトランザクションは、renameCollection
が完了するまで待機する必要があります。
どちらのシナリオでも、DDL 操作が maxTransactionLockRequestTimeoutMillis
を超えて保留中のままの場合、その操作の背後で待機している保留中のトランザクションは中止されます。つまり、maxTransactionLockRequestTimeoutMillis
の値は、少なくとも進行中のトランザクションと保留中の DDL 操作が完了するのに必要な時間をカバーする必要があります。
進行中のトランザクションと書込み競合 (write conflict)
トランザクションが進行中であり、トランザクションの外部への書込み (write) によって変更されたドキュメントに対して、後でトランザクション内の操作によって変更が試みられた場合、書込み競合 (write conflict) によりトランザクションは中止されます。
トランザクションが進行中で、ドキュメントを変更するためにロックがかかっている場合、トランザクション外部の書込み (write) が同じドキュメントを変更しようとすると、その書込み (write) はトランザクションが終了するまで待機します。
進行中のトランザクションと古い読み取り
トランザクション内の読み取り操作によって古いデータが返されることがあります。これをステイル読み取り(古いデータの読み取り)と呼びます。トランザクション内の読み取り操作では、コミットされた他のトランザクションによる書込み (write) やトランザクション以外の書込み (write) が反映されると保証されません。たとえば、次のシーケンスについて考えてみましょう。
あるトランザクションが進行中です。
トランザクション外の書込み (write) によりドキュメントが削除されます。
トランザクション内の読み取り操作では、書込み (write) 操作前のスナップショットが使用されるため、削除されたドキュメントを読み取ることができます。
単一ドキュメントのトランザクション内でのステイル読み取りを回避するには、db.collection.findOneAndUpdate()
メソッドを使用できます。次の mongosh
の例は、db.collection.findOneAndUpdate()
を使用して書込みロック (write lock) を取得し、読み取りが最新になるよう保証する方法を示しています。
トランザクション内での の使用db.collection.findOneAndUpdate()
employeeDoc = employeesCollection.findOneAndUpdate( { _id: 1, status: "Active" }, { $set: { lockId: ObjectId() } }, { returnNewDocument: true } )
トランザクション内では、findOneAndUpdate
操作によって新しい lockId
フィールドが設定されることに注意してください。lockId
フィールドは、ドキュメントが変更される限り、任意の値に設定できます。ドキュメントを更新することにより、トランザクションはロックを取得します。
トランザクションをコミットする前に、トランザクションの外部の操作によってドキュメントが変更されようとした場合、MongoDB は外部操作に書込み競合 (write conflict) エラーを返します。
進行中のトランザクションとチャンクの移行
チャンクの移行では、特定の段階で排他的なコレクション ロックが取得されます。
進行中のトランザクションでコレクションがロックされ、そのコレクションを含むチャンクの移行が開始された場合、これらの移行段階では、トランザクションがコレクションのロックを解放するまで待つ必要があるため、チャンクの移行のパフォーマンスに影響します。
チャンクの移行がトランザクションとインターリーブする場合(たとえば、チャンクの移行がすでに進行中のときにトランザクションが開始され、トランザクションがコレクションをロックする前に移行が完了した場合)、トランザクションはコミット中にエラーになり、中止されます。
2 つの操作がどのようにインターリーブされるかによって、次のようなエラーが出ます(エラーメッセージは省略されています)。
an error from cluster data placement change ... migration commit in progress for <namespace>
Cannot find shardId the chunk belonged to at cluster time ...
コミット中の外部読み取り
トランザクションのコミット中に、外部の読み取り操作が、トランザクションによって変更される同じドキュメントを読み込もうとする場合があります。トランザクションが複数のシャードに書き込まれる場合、シャード間でのコミット試行中に次のことが起きます。
読み取り保証 (read concern)
"snapshot"
または"linearizable"
を使用する外部読み取りは、トランザクションのすべての書込み (write) が表示可能になるまで待機します。因果的にコンシステントなセッション (afterClusterTime を含むもの)の一部である外部読み取りは、トランザクションのすべての書込みが表示可能になるまで待機します。
他の読み取り方法を使用した外部読み取りは、トランザクションのすべての書込みが表示されるまで待たずに、トランザクション前のバージョンのドキュメントを読み取ります。