分散ソフトウェア アーキテクチャ - 分散トランザクション TCC および SAGA

TCC事務

TCC は、もう 1 つの一般的な分散トランザクション メカニズムであり、 「 Try-confirm-Cancelです。

前に紹介した信頼性の高いメッセージ キューは、最終結果が比較的信頼性が高く、プロセスが (TCC と比較して) 十分に単純であることを保証できますが、プロセス全体にはまったく分離がなく、一部のビジネスでは分離は無関係です。隔離の欠如が多くの問題を引き起こす可能性がある場合。たとえば、この章のシナリオ例では、分離の欠如によって引き起こされる明らかな問題は「過剰販売」です。2 人の顧客が短期間に同じ製品を正常に購入し、それぞれの購入数量が異なっている可能性は十分にあります。現在の在庫を超える人はいませんが、購入の合計が在庫を超えています。この問題が厳格なトランザクション内にあり、分離レベルが十分である場合は、完全に回避できます。たとえば、上記のシナリオでは、ロックが原因で後で送信されたトランザクションが使用できないようにするために、分離レベル「Repeatable Read」が必要です。ただし、信頼できるメッセージ キューではこれを保証できません。この部分はデータベース ローカル トランザクションの知識に属しており、前の説明を参照できます。ビジネスで分離が必要な場合、アーキテクトは通常、強力な分離を必要とする分散トランザクションに適した TCC スキームに焦点を当てる必要があります。

具体的な実装に関して言えば、TCCはより煩雑であり、業務処理プロセスを「ビジネスリソースの予約」と「消費リソースの確認/解放」の2つのサブプロセスに分割する必要があるビジネス侵入型のトランザクションソリューションです。TCCの名前が示すように、TCCは以下の3つのフェーズに分かれています。

  • Try: 実行を試行する段階で、すべてのビジネス実行可能チェックを完了し (一貫性の保証)、必要なすべてのビジネス リソースを予約します (分離の保証)。
  • 確認: 実行フェーズを確認し、業務チェックは行わず、試行フェーズで準備したリソースを直接使用して業務処理を完了します。確認フェーズは繰り返し実行される可能性があるため、このフェーズで実行される操作は冪等である必要があります。
  • キャンセル: 実行フェーズをキャンセルし、試行フェーズで予約されたビジネス リソースを解放します。キャンセルフェーズは繰り返し実行される可能性があり、冪等である必要があります。

シナリオ例によれば、TCC の実行プロセスは図のようになります。
ここに画像の説明を挿入

  1. エンド ユーザーは、Fenix のブックストアにトランザクション リクエストを送信します。「Java 仮想マシンの詳細」を 100 元相当で購入します。
  2. トランザクションを作成し、トランザクション ID を生成してアクティビティ ログに記録し、試行フェーズに入ります。
  • ユーザーサービス: ビジネスの実現可能性を確認し、可能であればユーザーの 100 元を「凍結」状態に設定し、次のステップに確認段階に入るように通知し、不可能な場合は次のステップにキャンセル段階に入るように通知します。ステージ。
  • 倉庫サービス: ビジネスの実現可能性を確認し、可能であれば、倉庫内の「Java 仮想マシンの徹底理解」の 1 部を「凍結」状態に設定し、次のステップに確認段階に入るように通知します。実行できない場合は、次のステップにキャンセル段階に入るように通知します。
  • マーチャント サービス: リソースを凍結することなくビジネスの実現可能性を確認します。
  1. ステップ 2 のすべてのビジネスがビジネスが実行可能であると報告した場合、アクティビティ ログにステータスを 確認 として記録し、確認ステージに入ります。
  • ユーザーサービス: 業務運営を完了します (凍結された 100 元を差し引きます)。
  • 倉庫サービス: 業務を完了します (凍結された 1 冊の本を倉庫外としてマークし、対応する在庫を差し引きます)。
  • マーチャントサービス:業務運営を完了します(100元の支払い)。
  1. 3 番目のステップがすべて完了するとトランザクションは正常であると判断され、3 番目のステップでビジネス異常またはネットワーク異常の異常が発生した場合は、記録に基づいてサービスの確認操作が繰り返されます。アクティビティ ログ、つまり最大限の努力をして提供します。
  2. ステップ 2 で、いずれかの関係者がサービスが実行不可能であるとフィードバックした場合、またはいずれかの関係者がタイムアウトした場合は、アクティビティ ログのステータスを「キャンセル」として記録し、「キャンセル」ステージに入ります
  • ユーザーサービス:営業を中止(凍結された100元を解除)。
  • 倉庫サービス:営業停止(ブロックされている1冊を解除)。
  • 加盟店サービス:営業の中止(大泣きした後に加盟店に生計を立てるのを慰めるのは簡単ではない)。
  1. ステップ 5 が完了すると、トランザクションは失敗ロールバックで終了します。ステップ 5 でビジネス異常またはネットワーク異常に異常が発生した場合、アクティビティ ログの記録に従ってサービスのキャンセル操作が繰り返されます。 . つまり、ベストエフォート型配信です。

上記の操作プロセスから、TCC は実際には 2PC の準備フェーズと提出フェーズに少し似ていることがわかりますが、TCC はインフラストラクチャ レベルではなくユーザー コード レベルに位置しており、その実装に高い柔軟性をもたらします。必要に応じて実装できます。リソース ロックの粒度を設計します。TCC は、業務実行中に予約されたリソースのみを操作し、ロックやリソースの競合がほとんどなく、高いパフォーマンスの可能性を備えています。しかし、TCC は純粋に有益なわけではありません。また、より高い開発コストとビジネス侵入ももたらします。つまり、より高い開発コストと、トランザクション実装を置き換えるための置き換えコストがかかります。したがって、通常、私たちはベアコーディングに完全に依存することはありません。TCC を実装するには、以下に基づいて行われます。いくつかの分散トランザクション ミドルウェア (Ali によってオープンソース化されているSeataなど) を使用して、コーディングの作業負荷を可能な限り軽減します。

佐賀事情

TCC トランザクションは強力な分離機能を備えているため、「オーバーブッキング」の問題が回避され、そのパフォーマンスは一般に、この記事で説明したいくつかの柔軟なトランザクション モードの中で最高ですが、それでもすべてのシナリオを満たすことはできません。TCC の主な制限は、そのビジネスが非常に煩雑であることです。これは、前のセクションで説明した開発とコーディングの協力によってもたらされる作業負荷を繰り返すためではなく、制約が必要な技術的な制御可能性を指します。たとえば、シナリオを次のように変更します。 中国ではオンライン決済が普及しているため、ユーザーと販売者は書店システムでリチャージ口座を開設しないことを選択できます。少なくとも、銀行から書店へのリチャージを強制されることはありません。ショッピング時にUシールドまたはスキャンコードを介して直接支払い、銀行口座に支払いを転送することができます。この要件は中国におけるオンライン決済の一般的な状況と完全に一致していますが、システムのトランザクション設計に追加の制限が追加されます。ユーザーと加盟店の口座残高が銀行によって管理されている場合、その操作権限とデータ構造は制限されます。 , 通常、銀行は業務に協力してくれないため、資金の凍結、凍結解除、控除などの業務を完了することは不可能です。したがって、TCC の Try フェーズの最初のステップは実装できないことがよくあります。別の柔軟なトランザクション スキーム、SAGA トランザクションのみを検討できます。SAGAは英語で「長い物語、長い物語、長い一連の出来事」を意味します。SAGA トランザクションは、ロールバックではなくデータ補償に基づいています

SAGA トランザクション モデルの歴史は非常に長く、分散トランザクションの概念よりも古いものでした。これは、1987 年にプリンストン大学のヘクター・ガルシア・モリーナとケネス・セイラムが ACM に発表した論文「 SAGAS 」に由来しています。この論文では、「長期トランザクション」の運用効率を向上させる方法を提案します。一般的な考え方は、大規模なトランザクションを、インターリーブ可能な一連のサブトランザクション セットに分解することです。当初、SAGA の目的は、大規模なトランザクションによるデータベース リソースの長時間ロックを防ぐことでしたが、その後、分散環境における大規模なトランザクションを一連のローカル トランザクションに分解する設計パターンに発展しました。

SAGA は 2 つの操作部分で構成されます。

  1. 複数のサブトランザクションの分割
    大きなトランザクションはいくつかの小さなトランザクションを分割し、分散トランザクション T 全体を T1、T2、...、Ti、...、Tn という名前の n 個のサブトランザクションに分解します。各サブトランザクションはアトミックである必要があります、またはアトミックとみなすことができます。分散トランザクションが正常に送信できる場合、データへの影響 (最終的な整合性) は、Ti を継続的かつ順次送信した場合と同等になるはずです。
  2. 各サブトランザクションの補償アクションを設計
    する C1、C2、...、Ci、...、Cn という名前の各サブトランザクションに対応する補償アクションを設計します。Ti と Ci は次の条件を満たす必要があります。
    Ti と Ci は両方ともべき等です。
    Ti と Ci は交換法則 (Commutative) を満たします。つまり、Ti が先に実行されても、Ci が先に実行されても、結果は同じです。
    Ci は正常に送信できる必要があります。つまり、Ci 自体が送信に失敗してロールバックされる状況に関係なく、この状況が発生した場合は、成功するまで再試行し続ける必要があります。そうでない場合は、手動介入が必要になります。
    T1 から Tn が正常に送信された場合、トランザクションは正常に完了します。そうでない場合は、次の 2 つの回復戦略のいずれかを採用する必要があります。
  • フォワードリカバリ (フォワードリカバリ) : Ti トランザクションのコミットに失敗した場合、成功するまで Ti が再試行されます (ベストエフォート配信)。この回復方法は補償を必要とせず、トランザクションが最終的に成功する必要があるシナリオに適しています。たとえば、お金が他人の銀行口座から引き落とされた場合、そのお金を他人に発送する必要があります。フォワードリカバリの実行モードは、T1、T2、...、Ti(失敗)、Ti(リトライ)、...、Ti+1、...、Tnです。
  • 逆回復 (後方回復) : Ti トランザクションのコミットが失敗した場合、成功するまで Ci を実行して Ti を補います (ベストエフォート配信)。ここでは、Ci が (継続的な再試行の後) 正常に実行される必要があります。逆回復の実行モードは、T1、T2、...、Ti(失敗)、Ci(補償)、...、C2、C1です。

TCC と比較して、SAGA はリソースの凍結ステータスおよび凍結解除操作を設計する必要がなく、多くの場合、補償操作は凍結操作よりも実装がはるかに簡単です。例えば、上記の口座残高が銀行に直接保管されているシナリオでは、銀行からFenixのブックストアシステムに支払いが転送され、ユーザーの支払い操作を通じて銀行にサービスの提供を促します(スキャン コードまたは USB シールド); その後の業務運営が失敗した場合、銀行に以前のユーザー送金操作の取り消しを要求することはできませんが、Fenix のブックストア システムが支払いをユーザーの口座に振り戻すことは完全に可能です。補償措置。

SAGA は、すべてのサブトランザクションがコミットまたは補償されることを保証する必要がありますが、SAGA システム自体がクラッシュする可能性があるため、データベースと同様のログ メカニズム (SAGA ログと呼ばれる) として設計して、サブトランザクションが実行後に確実に追跡できるようにする必要があります。どのステップが実行されたか、どのステップが補正されたかなどの実行ステータス。さらに、補償操作は通常、凍結/取り消しよりも実装が簡単ですが、サービス オーケストレーションや信頼性の高いイベント キューなどを通じて、順方向および逆方向の回復プロセスを厳密に実行できるようにするには多大な労力がかかります。通常、トランザクションはベアコーディングによって直接実装されることはなく、トランザクションミドルウェアに基づいて完了するのが一般的であり、前述の Seata はSAGA トランザクションモードもサポートしています。

データ補償に基づいてロールバックを置き換えるというアイデアは、他のトランザクション スキームにも適用できます。これらのスキームについては個別のセクションを設けず、ここでまとめて説明します。具体例を挙げると、例えばAliのGTS(Global Transaction Service、SeataはGTSからオープンソース化されている)が提案する「ATトランザクションモード」がこれに該当する。

AT事務

全体として、AT トランザクションは XA 2 段階コミット プロトコルを参照して実装されていますが、XA 2PC の欠陥のため、つまり準備段階では、コーディネーターはコーディネーターよりも前にすべてのデータ ソースが成功を返すのを待つ必要があります。バケット効果 (関係するすべてのロックとリソースは、均一に解放される前に最も遅いトランザクションが完了するまで待つ必要がある)、およびターゲットを絞ったソリューションが設計されています。

一般的なアプローチは次のとおりです

  • ビジネス データが送信されるときにすべての SQL を自動的にインターセプトし、データ変更前後の SQL のスナップショットを保存し、行ロックを生成し、ローカル トランザクションを通じてそれらを運用データ ソースに送信します。これは、REDO を自動的に記録してロール ログを返すのと同等です。
  • 分散トランザクションが正常に送信された場合は、各データ ソース内の対応するログ データをクリーンアップするだけで十分です。分散トランザクションをロールバックする必要がある場合は、ログ データに基づいて補償用の「逆 SQL」を自動的に生成します。
  • この補償方法に基づいて、分散トランザクションに関与する各データ ソースを個別に送信することができ、ロックとリソースは即座に解放されます。この非同期送信モードでは、2PC と比較してシステムのスループット レベルが大幅に向上します。

その代償として、分離性が大幅に犠牲になり、原子性さえも直接的な影響を受けます
分離の欠如を前提とすると、ロールバックを補償に置き換えることが必ずしも成功するとは限らないためです。
たとえば、ローカル トランザクションがコミットされた後、分散トランザクションが完了する前に、データが補正される前に他の操作によってデータが変更される、つまりダーティ ライト (Dirty Write) が発生します。今回は不可能です。自動リバース SQL によって補償が実現されます。これは手動介入によってのみ処理できます。
一般に、ダーティ ライティングは回避する必要があります。ダーティ ライティングが発生すると人間が効果的に処理するのは実際には難しいため、従来のリレーショナル データベースはすべて、ダーティ ライティングを回避するために最も低い分離レベルでロックする必要があります。したがって、GTS は、書き込み分離を実現するために **「グローバル ロック」(グローバル ロック) のメカニズムを追加します**。ローカル トランザクションがコミットされる前に、送信が許可される前に、変更されたレコードのグローバル ロックを取得する必要があります。グローバル ロックが取得されません。以前はずっと待機しなければなりませんでした。この設計では、一定のパフォーマンスを犠牲にして、2 つの分散トランザクションに含まれるローカル トランザクションが同じデータを変更することを回避し、それによってダーティ ライトを回避します。

読み取り分離に関しては、AT トランザクションのデフォルトの分離レベルはRead Uncommitted (Read Uncommitted)であり、ダーティ リード (Dirty Read)が発生する可能性があることを意味します。グローバル ロック ソリューションを使用して読み取り分離の問題を解決することもできますが、読み取りを直接ブロックするとコストが非常に高くなるため、通常はこれを実行しません。分散トランザクションには万能のソリューションはなく、唯一の効果的な方法は、ローカルの状況に応じて適切なトランザクション処理ソリューションを選択することであることがわかります。

おすすめ

転載: blog.csdn.net/zkkzpp258/article/details/131423327