このトピックでは、ApsaraDB for MongoDBのトランザクションおよび読み書きに関するベストプラクティスについて説明します。
背景情報
MongoDB 4.0は、スタンドアロントランザクション (レプリカセットトランザクション) をサポートします。 レプリカセットインスタンスの1つ以上のコレクション内のトランザクションに対して操作を実行できます。 MongoDB 4.2は分散トランザクション (シャードトランザクション) をサポートします。 複数のコレクションの異なるドキュメントに対して、シャード間で操作を実行できます。
ApsaraDB for MongoDBは、常に単一のドキュメントに対する操作の原子性を保証します。 アプリケーションでは、埋め込みドキュメントと配列構造を使用して、ドキュメント構造の柔軟性により密接に関連付けられた単一のドキュメントを作成できます。 これは、構造ルールに従う複数のコレクションを作成し、複数のトランザクションを結合または更新する必要がある従来のリレーショナルデータベースを使用するよりもはるかに簡単です。 合理的なデータモデリングを伴うApsaraDB for MongoDBのほとんどのシナリオでは、単一のドキュメントに対する操作の原子性保証により、分散トランザクションの要件がなくなりました。
ただし、財務や会計などのいくつかの特別なシナリオには、分散トランザクションに対する強い要件が依然としてあります。 MongoDB 4.2以降は、分散ドキュメントを完全にサポートし、そのようなシナリオの要件を満たすことができます。
トランザクション
背景情報
ApsaraDB for MongoDBは、従来のリレーショナルデータベースと同じ方法で使用できます。 ApsaraDB for MongoDBのAPIは、従来のリレーショナルデータベースと同じ方法で使用できます。 学習曲線は含まれない。
トランザクションの例を次に示します。 API (startTransaction、abortTransaction、commitTransaction
) および関連するセッション
、読み取りの懸念、および書き込みの懸念
の設定を表示できます。
// コレクションを作成します。
db.getSiblingDB("mydb1").foo.insertOne ()
{abc: 0},
{writeConcern: { w: "majority", wtimeout: 2000 } }
)
db.getSiblingDB("mydb2").bar.insertOne ()
{xyz: 0} 、
{writeConcern: { w: "majority", wtimeout: 2000 } }
)
// セッションを開始します。
session = db.getMongo().startSession( { readPreference: { mode: "primary" } } );
coll1 = session.getDatabase("mydb1").foo;
coll2 = session.getDatabase("mydb2").bar;
// トランザクションを開始します。
session.startTransaction( { readConcern: { level: "local" }, writeConcern: { w: "majority" } } );
// トランザクションで操作を実行します。
try {
coll1.insertOne( { abc: 1 } );
coll2.insertOne( { xyz: 999 } );
} catch (エラー) {
// エラーが発生したときにトランザクションを中止します。
session.abortTransaction();
スローエラー。}
// トランザクションをコミットします。
session.com mitTransaction();
session.endSession();
トランザクションを使用する場合は、次の項目に注意してください。
トランザクションはセッションに関連付けられている必要があります。 セッションは、一度に1つの未処理トランザクションのみを有することができる。 セッションが終了した後、セッションに関連付けられた未処理のトランザクションがロールバックされます。
分散トランザクションは、異なるコレクション内の異なるドキュメントに対する操作を同時に含むことができる。
トランザクションが実行されると、トランザクションは、トランザクションによってコミットされていない書き込み操作を読み取ることができる。 ただし、トランザクションの外部の他の操作は、トランザクションでコミットされていない書き込み操作を読み取ることができません。
トランザクションに書き込まれたコミットされていないデータは、トランザクションがコミットされるまでセカンダリノードにレプリケートされません。 トランザクションがコミットされると、トランザクションに書き込まれたデータはレプリケートされ、レプリカセットインスタンスのすべてのセカンダリノードに自動的に適用されます。
ドキュメントを変更すると、トランザクションはドキュメントをロックして、トランザクションが完了するまでドキュメントが他の操作によって変更されないようにします。 トランザクションは、別のトランザクションがすでにロックを保持している可能性があるため、トランザクションが変更する必要があるドキュメントのロックを取得できません。 この場合、トランザクションは中止され、5ミリ秒後に書き込み競合が報告されます。 トランザクションの中止時間は、maxTransactionLockRequestTimeoutMillisカーネルパラメーターによって指定されます。 パラメーターの詳細については、「maxTransactionLockRequestTimeoutMillis」をご参照ください。
トランザクションは再試行メカニズムの対象となります。 一時的なネットワーク中断などの一時的な再試行エラーが報告された場合、トランザクションは自動的に再試行されます。 クライアントは再試行操作を認識していません。
トランザクションにはライフサイクルがあります。 60秒を超えて実行されるトランザクションは、バックグラウンドスレッドによって強制的に中止されます。 トランザクションの強制中止時間は、transactionLifetimeLimitSecondsカーネルパラメーターで指定します。 パラメーターの詳細については、「transactionLifetimeLimitSeconds」をご参照ください。
制限
分散トランザクションでは、新しいコレクションとインデックスを作成できません。
トランザクションをキャップ付きコレクションに書き込むことはできません。
トランザクションでは、スナップショット読み取りコンサートレベルを使用して上限付きコレクションを読み取ることはできません。 これはMongoDB 5.0以降に適用されます。
トランザクションは、
config/admin/local
という名前のデータベースのコレクションを読み書きできません。トランザクションは、
system.*
形式でシステムコレクションにデータを書き込むことはできません。トランザクションは
説明
をサポートしていません。トランザクションは、
getMore
を使用して、トランザクションの外部で作成されたカーソルを読み取ることはできません。 トランザクションで作成されたカーソルも、トランザクションの外部でgetMore
を使用して読み取ることはできません。トランザクションの最初の操作を
killCursors/hello
にすることはできません。listCollections、listIndexes、createUser、getParameter、count
などのCRUD以外のコマンドは、トランザクションでは実行できません。分散トランザクションのシャードに対して、writeConcernMajorityJournalDefaultパラメーターをfalseに設定することはできません。 パラメーターの詳細については、「writeConcernMajorityJournalDefault」をご参照ください。
分散トランザクションは、アービタを含むシャードをサポートしません。 アービタの詳細については、「生産上の考慮事項」をご参照ください。
ベストプラクティス
分散トランザクションの代わりにスタンドアロントランザクションを使用する
ほとんどのシナリオでは、分散トランザクションは、トランザクションを含まないスタンドアロントランザクションまたは書き込み操作よりもパフォーマンスが低下します。 これは、トランザクションを含む操作がより複雑な操作を必要とするためです。 ApsaraDB for MongoDBのデータモデリングには、非正規化されたデータモデル (埋め込みドキュメントと配列構造) が最適です。 合理的なデータモデリングとスタンドアロントランザクションは、ほとんどのシナリオでアプリケーションのトランザクション要件を完全に満たすことができます。
長期にわたるトランザクションの回避
デフォルトでは、ApsaraDB for MongoDBは、60秒を超えて実行される分散トランザクションを自動的に中止します。 タイムアウトの問題を解決するには、トランザクションを小さな部分に分割して、指定された期間内にトランザクションを実行します。 クエリステートメントが最適化されていることを確認し、トランザクション内のデータにすばやくアクセスできる適切なインデックスカバレッジを提供します。
トランザクション内の大量のドキュメントを変更しないでください
ApsaraDB for MongoDBは、トランザクションで読み取ることができるドキュメントの数に制限を課しません。 ただし、トランザクション内の多数のドキュメントを変更すると、プライマリノードとセカンダリノード間のデータ同期のワークロードが増加し、セカンダリノードで同期の遅延やその他の問題が発生する可能性があります。 トランザクションで最大1,000個のドキュメントを変更することを推奨します。 トランザクション内の1,000を超えるドキュメントを変更する場合は、トランザクションを複数の部分に分割し、ドキュメントをバッチで変更することをお勧めします。
サイズが16 MBを超える大規模なトランザクションを実行しない
MongoDB 4.0では、トランザクションは単一のoplogエントリで表されます。 エントリのサイズは16 MB以内でなければなりません。 ApsaraDB for MongoDBでは、更新操作の場合は増分コンテンツを記録し、挿入操作の場合はドキュメント全体を記録します。 したがって、トランザクション内のすべてのステートメントのすべてのoplogレコードのサイズは16 MB以内でなければなりません。 この制限を超えると、トランザクションは中止され、完全にロールバックされます。 大規模なトランザクションを、それぞれサイズが16 MB以下の小さな操作セットに分割することを推奨します。
MongoDB 4.2以降は、すべての書き込み操作をトランザクションに格納するために複数のoplogエントリを作成します。 このようにして、単一のトランザクションのサイズが16 MBを超えることがあります。 ただし、トランザクションサイズを16 MB以内に抑えることを推奨します。 大規模なトランザクションは他の問題を引き起こす可能性があります。
クライアントでトランザクションのロールバックを処理するための適切なロジックの設計
トランザクションが異常に中止されると、例外がドライバに返され、トランザクションがロールバックされます。 プライマリ /セカンダリの切り替えやノードの障害などの一時的な例外が原因で中止されたトランザクションをキャプチャして再試行するためのロジックをアプリケーションに追加します。 ApsaraDB for MongoDBが提供するドライバーは、再試行可能な書き込みを使用して、トランザクションを自動的に再試行します。 ただし、アプリケーションでは、TransactionTooLarge
、TransactionTooOld
、TransactionExceededLifetimeLimitSeconds
などの再試行可能な書き込みでは解決できないトランザクション例外とエラーを処理する必要があります。 再試行可能な書き込みの詳細については、「再試行可能な書き込み」をご参照ください。
トランザクションでのDDL操作の回避
createIndex
やdropDatabase
などのコレクションに対するDDL操作は、コレクションで実行されるアクティブなトランザクションによってブロックされます。 この場合、同じコレクションにアクセスしようとするすべてのトランザクションは、指定された時間内にロックを取得できないため、新しいトランザクションが中止されます。
MongoDB 4.4以降は、shouldMultiDocTxnCreateCollectionAndIndexesパラメーターで指定される関連制限を最適化します。 分散トランザクションでcreateCollection
またはcreateIndex
操作を実行できます。 しかし、この操作には以下の限界がある。 MongoDB 4.4以降のトランザクションの詳細については、「トランザクション」をご参照ください。 パラメーターの詳細については、「MongoDB Serverパラメーター」をご参照ください。
暗黙的に作成できるのはコレクションのみです。
存在しないコレクションに対して操作を実行できます。
データを含まないコレクションに対して操作を実行できます。
したがって、トランザクションでのDDL操作は避けることをお勧めします。
コミットしたくないトランザクションと、できるだけ早い機会にエラーがあるトランザクションをロールバック
コミットされていないトランザクションのすべての変更は、WiredTigerキャッシュに保存されます。 コミットしたくない複数のトランザクションとエラーのあるトランザクションがシステムに含まれている場合、WiredTigerキャッシュの負荷が高くなり、他の問題が発生する可能性があります。 トランザクションの操作期間を制御し、できるだけ早くリソースをリリースする機会にコミットしたくないトランザクションをロールバックします。
タイムアウトのためにトランザクションが頻繁にロールバックされる場合、タイムアウトに関連するパラメーターの値を増やしてロックを取得
既定では、トランザクションの操作が5ミリ秒以内に必要なロックを取得できない場合、トランザクションは自動的にロールバックされます。 トランザクションがロールバックまたはコミットされると、トランザクションはすべての占有ロックを解放します。 ロックを取得するためのタイムアウトによりトランザクションが頻繁にロールバックされる場合は、maxTransactionLockRequestTimeoutMillisパラメーターの値を大きくします。 詳細については、「maxTransactionLockRequestTimeoutMillis」をご参照ください。
問題が解決しない場合は、トランザクションの操作を確認し、DDL操作や最適化するクエリなど、ロックを長時間占有する可能性のある操作がトランザクションに含まれているかどうかを確認します。
トランザクション内外で同じドキュメントを変更することによる書き込み競合の回避
進行中のトランザクション以外の書き込み操作がドキュメントを変更し、トランザクション内の操作もドキュメントを変更しようとすると、書き込みの競合によりトランザクションはロールバックされます。 進行中のトランザクションがドキュメントを変更するのに必要なロックを取得した場合、外部書き込み操作は、トランザクションが終了するまで待機してからドキュメントを変更する必要があります。
書き込み競合が発生しても、トランザクション外の書き込み操作が失敗したり、クライアントにエラーが返されたりすることはありません。 ApsaraDB for MongoDBは引き続き操作を再試行し、操作が成功するまで毎回writeConflicts
カウンターをインクリメントします。 クライアントは例外を認識していません。 ただし、リクエストの応答には長い時間がかかります。
ほとんどの場合、少数の書き込み競合は大きな影響を与えません。 ただし、多数の書き込み競合が発生すると、データベースのパフォーマンスが低下する可能性があります。 監査ログまたはスロークエリログを使用して、大量の書き込み競合が発生したかどうかを確認できます。
カーネルリスク
実行時間の長いトランザクションを作成したり、トランザクション内で大量の操作を実行しようとすると、WiredTigerキャッシュの負荷が高くなります。これは、コミットされていないトランザクション内の後続のすべての書き込み操作について、キャッシュがデータを格納し、データステータスを維持する必要があるためです。 進行中のトランザクションは同じスナップショットを使用します。 したがって、トランザクションの実行中に、新しい書き込み操作がWiredTigerキャッシュに蓄積され続けます。 WiredTigerキャッシュの書き込み操作は、古いスナップショットで実行されたトランザクションがコミットまたは中止されるまで削除できます。 ほとんどの場合、長時間のトランザクションによって引き起こされるWiredTigerキャッシュの過負荷により、データベースのストール、応答レイテンシの大幅な増加、CPU使用率の高さ、さらにはビジネスに影響を与えるデッドロックなど、より多くの問題が発生します。 キャッシュの過負荷は、WiredTigerストレージエンジンのキャッシュ使用率とダーティキャッシュ使用率が指定されたしきい値を超えると発生します。 カーネルリスクの詳細については、「SERVER-50365」および「SERVER-51281」をご参照ください。
リスクを防ぐために、長時間実行されるトランザクションを頻繁に処理したり、トランザクションで大量の操作を実行したりするApsaraDB for MongoDBインスタンスをMongoDB 5.0にアップグレードすることを推奨します。
懸念事項を読む
背景情報
Read Concernは、次のデータ一貫性と分離レベルを提供します。 詳細については、「関連情報の読み取り」をご参照ください。
"local"
: レプリカセットインスタンスのプライマリノードまたはセカンダリノードに対する読み取りのデフォルト。 データはローカルデータベースから読み取られます。 ロールバックされたデータを読み取ることができる。"available"
: シャードクラスタインスタンスのセカンダリノードに対する読み取りのデフォルト。 ロールバックされたデータを読み取ることができる。 データが読み取られる前に、シャードバージョンはチェックされません。 したがって、孤立した文書を読むことができます。 レベルは、最小のアクセス遅延を提供します。「majority」
: 大多数のノードによって肯定応答されたデータが読み取られる。 データはロールバックされません。"linearizable"
: 最高のデータ一貫性を必要とする線形化レベル。 読み出し動作は、全ての書き込み動作が大多数のノードによって確認されるまで待たなければならない。 レベルのパフォーマンスは最も低く、プライマリノードでのみ使用できます。「スナップショット」
: 大多数のノードによって肯定応答されるデータは、スナップショットに基づいて読み取られる。 特定の時点におけるスナップショットは、読み出し動作に関連付けることができる。 atClusterTimeパラメーターを設定して、読み取り操作のタイムスタンプを指定できます。 パラメーターの詳細については、「関連の読み取りおよびatClusterTime」をご参照ください。
以下の点にご注意ください。
mongosノードの最新データは、読み取りの懸念レベルに関係なく、レプリカセットインスタンスの最新バージョンのデータを表すものではありません。
操作ごとに異なる読み取り関係レベルを指定できます。 サーバー上でMongoDB 4.4以降を実行するインスタンスの既定の読み取り懸念レベルを指定することもできます。 操作の読み取り関連レベルは、サーバー上のものよりも高い優先度を持っています。
ローカル
データベースからデータを読み取る場合、指定した読み取り関係レベルは無視されます。 この場合、ローカル
データベースからすべてのローカルデータをいつでも読み取ることができます。分散トランザクションは、
"local"
、"majority"
、および"snapshot"
読み取り関連レベルのみをサポートします。因果的に一貫したセッションは、
「過半数」
の読み取り懸念レベルのみをサポートします。
ベストプラクティス
分散トランザクションに対してのみ読み取り懸念レベルを指定する
分散トランザクションの操作ごとに読み取り関係レベルを指定する必要はありません。 トランザクションの読み取り関連レベルは、他の設定またはデフォルトの読み取り関連レベルを上書きします。
Read Concernは、Write Concernと同様に、クエリが単一のドキュメントに対して実行されるか、ドキュメントのセットに対して実行されるか、複数のドキュメント読み取りトランザクションにカプセル化されるかにかかわらず、データベース上のすべてのクエリに適用できます。
"majority"
を使用するほとんどの場合
データの一貫性と分離を確実にするために、読み取りに関する "majority"
レベルを使用することを推奨します。 アプリケーションは、データがレプリカセットインスタンスの大多数のノードにレプリケートされている場合にのみ、データを読み取ることができます。 したがって、ノードがプライマリノードとして選択された場合、データはロールバックされません。
プライマリノードからデータを読み取り、"local"
を使用するか、"linearizable"
をするか、独自の書き込みが読み取られるシナリオでの読み取り関係レベル
書き込み操作が完了した後、書き込み済みの変更をできるだけ早く読み取るには、プライマリノードからデータを読み取り、"local"
または "linearizable"
読み取り関連レベルを使用します。 「多数決」
の書き込み関連レベルが使用される場合、「多数決」
の読み出し関連レベルを使用することができる。
シナリオでMongoDB 3.6以降を実行するインスタンスには、因果的に一貫したセッションを使用できます。
"linearizable"
読み取り関連レベルが最高のデータ一貫性を必要とするシナリオで使用されている場合は、 maxTimeMS
パラメーターを設定します。
「linearizable」
の読み取り関連レベルは、ノードからデータを読み取るときにレプリカセットインスタンスのプライマリノードがインスタンスのプライマリノードであり、ノードが新しいプライマリノードとして選択されてもノードから返されたデータがロールバックされないことを保証します。 ただし、このレベルはレイテンシに大きな影響を与えます。 maxTimeMS
パラメーターを設定して、大多数のノードに障害が発生した場合に読み取り操作が無期限にブロックされないようにします。
Write Concern
背景情報
書き込み懸念の詳細については、「書き込み懸念」をご参照ください。
{ w: <value>, j: <boolean>, wtimeout: <number> }
データ永続性の保証レベルを指定する書き込み懸念は、次のレベルを提供します。
{w: 0}
: 書き込み操作が完了したことが確認されず、書き込まれたデータが失われる可能性があります。{w: 1}
: 書き込み操作の完了が確認されました。 MongoDB 5.0より前のバージョンを実行するインスタンスのデフォルト。 データが永続化されないため、データ損失が発生する可能性があります。{j: true}
: 書き込み操作が完了したことが確認され、永続的に格納されたWALログにフラッシュされます。 操作は失われません。{ w: "majority" }
: 書き込み操作は、操作がレプリカセットインスタンスの大多数のノードにレプリケートされるまで確認できます。 データはロールバックされません。 MongoDB 5.0以降を実行するインスタンスのデフォルト。レプリカ確認: 書き込み操作は、操作がレプリカセットインスタンス内の特定の数のノードにレプリケートされるまで確認できます。
カスタム承認: settings.getLastErrorModesパラメーターを設定して、タグを使用して他のカスタム承認方法を指定します。 パラメーターの詳細については、「settings.getLastErrorModes」をご参照ください。
以下の点にご注意ください。
書き込み操作またはトランザクションの書き込み関連レベルを指定できます。 レベルを指定しない場合は、デフォルトの書き込み関連レベルが使用されます。
説明MongoDB 5.0以降を実行し、標準の3レプリカトポロジアーキテクチャを使用するインスタンスでは、デフォルトのグローバル書き込み懸念レベルが
{w:1}
から{w:"majority"}
に変更されます。 これにより、インスタンスをMongoDB 5.0にアップグレードした後にパフォーマンスが低下する可能性があります。非表示ノード、レイテンシノード、またはレプリカセットインスタンス内の優先度が0である他のノードは、
「多数決」
の書き込み懸念レベルで指定されたノードのメンバーと見なすことができます。操作ごとに異なる書き込み懸念レベルを指定できます。 また、サーバー上でMongoDB 4.4以降を実行するインスタンスの既定の書き込み懸念レベルを指定することもできます。 オペレーションの書き込み関連レベルは、サーバ上のものよりも高い優先度を有する。
データが
ローカル
データベースに書き込まれると、指定した書き込み関連レベルは無視されます。因果的に一貫したセッションは、
「過半数」
の書き込み懸念レベルのみをサポートします。
ベストプラクティス
分散トランザクションのみの書き込み懸念レベルを指定する
トランザクション内の各書き込み操作に書き込み懸念レベルを指定する必要はありません。 それ以外の場合は、エラーが返されます。
一般的なシナリオで "majority"
書き込み懸念レベルを使用する
「多数決」
の書き込み懸念レベルは、レプリカセットインスタンス内のノードの大多数が書き込み動作を確認することを保証する。 ノード障害が発生した場合、またはプライマリ /セカンダリ切り替えで例外が発生した場合、データの損失またはロールバックは発生しません。
最高の書き込みパフォーマンスが必要なシナリオで {w:1} の書き込み懸念レベルを使用し、セカンダリノードのレプリケーション遅延に重点を置く
ほとんどの場合、{w:1}
の書き込み関連レベルは、より高い書き込みパフォーマンスを提供し、書き込みの頻度が高いシナリオに適しています。 セカンダリノードのレプリケーション待ち時間に注意してください。 レプリケーションの待ち時間が長い場合、プライマリノードのロールバックに失敗する可能性があります。 詳細については、「ROLLBACK」をご参照ください。 レプリケーションのレイテンシがoplogの保持期間を超えると、セカンダリノードは異常な回復状態になり、障害から自動的に回復できず、インスタンスの可用性が低下します。 状態の詳細については、「回復」をご参照ください。
上記の問題は、大量のデータをインスタンスに一括書き込みするか、data Transmission Service (DTS) を使用してデータをインスタンスに移行する場合に、MongoDB 5.0より前のバージョンを実行するApsaraDB for MongoDBインスタンスで発生する可能性があります。 この問題を解決するには、書き込みに関する "majority"
レベルを使用することを推奨します。
操作の適切な書き込み懸念レベルの指定
操作の要件に基づいて、操作の書き込み懸念レベルを指定します。 たとえば、財務トランザクションデータに書き込み懸念レベルが指定されているトランザクションを使用してアトミック性を確保し、コアプレーヤーのデータに "majority"
書き込み懸念レベルを使用してデータがロールバックされないようにし、ログデータにはデフォルトまたは {w:1}
書き込み懸念レベルを使用できます。
ApsaraDB for MongoDBは、ビジネス要件に基づいて書き込み懸念レベルを指定できる柔軟性を強化します。