はじめに
トランザクションは、データベースにおける論理的な処理グループであり、複数のドキュメントにわたる読み取りや書き込みなどの1つまたは複数の操作をカプセル化します。個々のステートメントで実行されるのではなく、データベースは操作のグループをまとまりのあるユニットとして解釈し、処理することができます。これは、密接に関連する多くのステートメントの過程でデータセットの一貫性を確保するのに役立ちます。
このガイドでは、まずトランザクションとは何か、ドキュメントモデルでトランザクションをいつ使用するか、そしてMongoDBでトランザクションが概念的にどのように機能するかについて説明します。
MongoDBを使用している場合は、PrismaのMongoDBコネクタをチェックしてください!Prisma Clientを使用すると、本番環境のMongoDBデータベースを自信を持って管理できます。
MongoDBとPrismaの操作を開始するには、ゼロからのスタートガイドまたは既存のプロジェクトに追加する方法をご覧ください。
トランザクションとは?
トランザクションは、複数のステートメントをグループ化して単一の操作として処理するための方法です。トランザクションでは、各コマンドがサーバーに送信されると個別に実行されるのではなく、コマンドがまとめてバンドルされ、他のリクエストとは別のコンテキストで実行されます。
分離性は、トランザクションの重要な部分です。トランザクション内では、実行されたステートメントはトランザクション自体の環境内でのみ影響を与えることができます。トランザクション内から、ステートメントはデータを変更でき、その結果はすぐに表示されます。外部からは、トランザクションがコミットされるまで変更は行われず、その時点でトランザクション内のすべてのアクションが一度に表示されます。
これらの機能は、原子性と分離性を提供することにより、データベースがACID準拠を達成するのに役立ちます。これらは共に、データベースが一貫性を維持するのに役立ちます。さらに、トランザクションの変更は、不揮発性ストレージにコミットされるまで成功として返されず、永続性を提供します。
MongoDBは、マルチドキュメントACIDトランザクションと分散マルチドキュメントACIDトランザクションをサポートしています。本質的に、ドキュメントモデルでは、関連するデータを単一のドキュメントにまとめて格納できます。ドキュメントモデルは、原子的なドキュメント更新と組み合わせて、ほとんどのユースケースでトランザクションの必要性をなくします。ただし、マルチドキュメント、マルチコレクションのMongoDBトランザクションが最良の選択となるケースもあります。
ドキュメントモデルでトランザクションを使用する場合
ドキュメントモデルは、トランザクションが対象とするニーズの多くを本質的に解決します。それにもかかわらず、ドキュメントモデルでもトランザクションを利用する必要があるユースケースがいくつか際立っています。
トランザクションを必要とするアプリケーションは、通常、異なる当事者間で値が交換されるアプリケーションです。例としては、"System of Record"または"Line of Business"アプリケーションなどが挙げられます。
マルチドキュメントトランザクションの利点は、銀行アプリケーションや決済処理、商品の所有権が移転されるサプライチェーンや輸送システム、またはeコマースなど、資金を移動するシステム向けに調整されています。これらの例は一般的に、複数のストリームにわたる変更をまとめてパッケージ化し、トランザクションが提供するオールオアナッシングのアプローチで強力な一貫性を必要とします。
トランザクションの使用方法
MongoDBは、トランザクションを使用するための2つのAPIを提供しています。1つ目は、リレーショナルデータベースと同様の構文を持つコアAPIです。2つ目のコールバックAPIは、MongoDBでトランザクションを使用するための推奨アプローチです。
2つのAPIの比較は、次の表に最もよくまとめられています。
コアAPI | コールバックAPI |
---|---|
トランザクションを開始およびコミットするための明示的な呼び出しが必要 | トランザクションを開始し、指定された操作を実行し、コミット(またはエラー時に中止)します |
TransientTransactionError およびUnknownTransactionCommitResult のエラー処理ロジックを自動的に組み込みませんが、代わりにカスタムエラー処理を統合する機能を提供します。 | TransientTransactionError およびUnknownTransactionCommmitResult のエラー処理ロジックを自動的に組み込みます。 |
特定のトランザクションのために、明示的な論理セッションをAPIに渡す必要があります。 | 特定のトランザクションのために、明示的な論理セッションをAPIに渡す必要があります。 |
MongoDBとPrismaの操作を開始する場合は、ゼロからのスタートガイドまたは既存のプロジェクトに追加する方法をご覧ください。
トランザクションセッションの作成
トランザクションは通常、アプリケーション言語の適切なMongoDBドライバーを使用して、APIメソッドの1つを介して外部アプリケーションから記述および実行されます。
デモンストレーションのために、MongoDBシェルを介してトランザクションを作成する手順を説明します。トランザクションが実際にどのように機能するかを概念化するために、サンプルデータベースとコレクションを使用します。
注:トランザクションセッションが最初のstartTransaction()
メソッドから60秒を超えて実行されると、MongoDBは自動的に操作を中止します。これは、トランザクションが通常、シェルでコマンドを発行する人ではなく、自動的に動作するアプリケーションで実行されるためです。次の手順は、トランザクションセッションを最初から最後まで概念化するためのものです。
まず、literature
という名前のシンプルなデータベースと、authors
という名前のコレクションがあります。簡単なfind()
クエリを実行すると、著者名と彼らのタイトルのドキュメント構造を確認できます。
db.authors.find()
[{_id: ObjectId("620397dd4b871fc65c193106"),first_name: 'James',last_name: 'Joyce',title: 'Ulysses'},{_id: ObjectId("620398016ed0bb9e23785973"),first_name: 'William',last_name: 'Gibson',title: 'Neuromancer'},{_id: ObjectId("6203981d6ed0bb9e23785974"),first_name: 'George',last_name: 'Orwell',title: 'Homage to Catalonia'},{_id: ObjectId("620398516ed0bb9e23785975"),first_name: 'James',last_name: 'Baldwin',title: 'The Fire Next Time'}]
データベースができたので、MongoDBシェルで直接トランザクションを操作する手順を示すことができます。また、あるセッションで実行されているトランザクションが外部ソースに検出されない可能性があることも示します。
まず、APIを使用する場合と同様に、セッションを作成する必要があります。
var session = db.getMongo().startSession()
新しく作成されたsession
変数は、セッションオブジェクトを格納します。
次のステップは、startTransaction()
メソッドを呼び出してトランザクションを開始することです。
session.startTransaction({ "readConcern": { "level": "snapshot" },"writeConcern": { "w": "majority }})
startTransaction()
メソッドには、readConcern
とwriteConcern
の2つのオプションがあります。これらのオプションの詳細については、MongoDBのドキュメントを参照してください。readConcern
level
をsnapshot
に設定します。これは、トランザクションがwriteConcern
"majority"でコミットされた場合、多数決コミットされたデータのスナップショットからデータを返します。
w: "majority"
書き込み確認と"snapshot"
の読み取り確認レベルでコミットすると、操作が多数決コミットされたデータの同期されたスナップショットを持つことが保証されます。この書き込みおよび読み取り確認の構成は、特定要件がない場合の適切なデフォルトと考えることができます。
メソッドは成功した場合何も返しません。エラーが発生した場合は、トランザクションを中止する必要があります。これについては後で説明します。
トランザクションセッション内での作業
トランザクションセッションが開始されたので、前のセクションのsession
変数のコンテキスト内でステートメントを実行する必要があります。
構文を簡単にするために、セッション内の変数でコレクションを表すと役立ちます。これは次のように行うことができます。
var authors = session.getDatabase('literature').getCollection('authors')
この新しく作成された変数は、トランザクションセッション外のシェルで作業する場合と同じようにdb.authors
として機能します。これを確認するには、2つ目のシェルウィンドウを開き、クラスターに接続してdb.authors.find()
を実行します。どちらのステートメントも同じドキュメントを返します。
セッション内で、外部アプリケーションが行う可能性のあることをシミュレートし、データベースにレコードを追加します。これは、authors
コレクションに対して次の方法で実行できます。
authors.insertOne( {"first_name": "Virginie","last_name": "Despentes","title": "Vernon Subutex") })
MongoDBは成功を返します。
{acknowledged: true,insertedId: ObjectId("6203a075c374636bc6976baa")}
セッションでauthors.find()
を実行すると、追加を含めて以前の結果が返されます。
[{_id: ObjectId("620397dd4b871fc65c193106"),first_name: 'James',last_name: 'Joyce',best_title: 'Ulysses'},{_id: ObjectId("620398016ed0bb9e23785973"),first_name: 'William',last_name: 'Gibson',best_title: 'Neuromancer'},{_id: ObjectId("6203981d6ed0bb9e23785974"),first_name: 'George',last_name: 'Orwell',best_title: 'Homage to Catalonia'},{_id: ObjectId("620398516ed0bb9e23785975"),first_name: 'James',last_name: 'Baldwin',best_title: 'The Fire Next Time'},{_id: ObjectId("6203a075c374636bc6976baa"),first_name: 'Virginie',last_name: 'Despentes',best_title: 'Vernon Subutex'}]
このトランザクションセッションはまだコミットされていないため、セッションの外部にあるMongoDBシェルでは同じ結果が返されません。確認するには、別のMongoDBシェルインスタンスでdb.authors.find()
を実行すると、元のドキュメントが返されます。
[{_id: ObjectId("620397dd4b871fc65c193106"),first_name: 'James',last_name: 'Joyce',best_title: 'Ulysses'},{_id: ObjectId("620398016ed0bb9e23785973"),first_name: 'William',last_name: 'Gibson',best_title: 'Neuromancer'},{_id: ObjectId("6203981d6ed0bb9e23785974"),first_name: 'George',last_name: 'Orwell',best_title: 'Homage to Catalonia'},{_id: ObjectId("620398516ed0bb9e23785975"),first_name: 'James',last_name: 'Baldwin',best_title: 'The Fire Next Time'}]
この違いは、トランザクションが現在もあいまいな状態にあることを示しています。トランザクションは成功してデータベースにコミットされることも、失敗して中止されることもあります。いずれにせよ、データベースは最終的に一貫性のある状態になります。新しいレコードが含まれるか、トランザクションセッション開始前と同じ状態にロールバックされます。
セッションでcommitTransaction()
メソッドを実行してトランザクションをコミットし、データベースを新しい一貫性のある状態にします。
session.commitTransaction()
これで、トランザクションセッションが行われたMongoDBシェルと、トランザクション外のMongoDBシェルの両方のインスタンスに新しいレコードが存在します。
トランザクションの中止
セッションの開始からコミットまでの流れを説明したので、トランザクションの代替結果を探ることができます。
このパスは同じように始まり、コミットポイントでのみ変更されます。トランザクションが行っている変更を破棄する場合は、セッションシェルで次のようにabortTransaction()
メソッドを使用します。
session.abortTransaction()
このメソッドはトランザクションセッションをキャンセルし、潜在的な変更を破棄します。新しい一貫性のある状態になる代わりに、データベースは変更をロールバックし、トランザクションセッションが最初に開始されたときと同じ状態を維持します。
結論
このガイドでは、トランザクションとは何か、そしてMongoDBでトランザクションが最適なユースケースについて説明しました。また、MongoDBシェルでのトランザクションセッションのプロセスを概念的に説明し、外部アプリケーションがどのように動作するかを理解できるようにしました。
トランザクションは、リレーショナルデータベースの基本的なニーズであり、MongoDBなどのドキュメントモデルデータベースの特定のユースケースにも必要です。ドキュメントモデルに関して言えば、一般的には、一緒にアクセスされるデータは一緒に格納する必要があることが推奨されます。ただし、そうでない場合もあるため、トランザクションの基本を知っておくことが重要です。
MongoDBを使用している場合は、PrismaのMongoDBコネクタをチェックしてください!Prisma Clientを使用すると、本番環境のMongoDBデータベースを自信を持って管理できます。
MongoDBとPrismaの操作を開始するには、ゼロからのスタートガイドまたは既存のプロジェクトに追加する方法をご覧ください。
FAQ
はい、MongoDBトランザクションはNode.jsで使用できます。
MongoDBは、「Node.jsでMongoDBトランザクションを使用する方法」に関する役立つスタートガイドを提供しています。
ACIDは、原子性、一貫性、分離性、および永続性の頭字語です。これらのプロパティはすべて、データベースを有効な状態に保つことに関係しています。
トランザクションは、データベース操作のグループが一緒に行われるため、予期しないエラーが発生した場合にデータベースの有効性を確保することが重要です。ACID準拠のトランザクションは、無効なデータベース状態から保護します。
MongoDBトランザクションはセッション内に存在します。startSession()
を使用してセッションを作成し、トランザクション操作をコミットするためにsession.startTransaction()
を続けます。
これらの変更を意図的にロールバックするには、session.abortTransaction()
メソッドを使用して、セッションで開始された操作を破棄できます。これは、session.commitTransaction()
メソッドの前に実行する必要があります。
MongoDBを使用する場合に考慮すべきトランザクションのベストプラクティスがいくつかあります。MongoDBは60秒を超えるものを自動的に中止するため、トランザクションの実行時間を開始から60秒以内に抑えるように最適化する必要があります。
トランザクション内の操作数は、トランザクション内で変更されるドキュメントが1,000個を超えないようにする必要があります。さらに、トランザクション内で複数の操作が発生するため、適切な読み取りおよび書き込み確認を選択することが重要です。MongoDBは、これらおよびその他多くのことを網羅した詳細なベストプラクティスガイドを提供しています。
はい、MongoDBはMongoDB 4.0以降のバージョンでマルチドキュメントACIDトランザクションをサポートしています。