文章 この問題について考え始めた当初の目的は、ある時友人に送金したのですが、私のお金が差し引かれてしまい、友人はお金を受け取れなかったことでした。私は、銀行振込は強い一貫性を確保するためにトランザクションによって保証される必要があると常に考えており、分散トランザクションのさまざまな理論と方法を研究してまとめました。 不倫は非常に広い意味を持つ言葉であり、業界によって解釈が異なります。プログラマーにとって、トランザクションはトランザクションと同等であり、一連の連続した操作が論理的かつ完全な操作に結合されます。つまり、この一連の操作が実行される前と実行後に、システムは予測可能で一貫した状態である必要があります。したがって、この一連の操作はすべて正常に実行されるか、まったく実行できないかのいずれかになります。一部が成功し、一部が失敗した場合は、成功した部分をロールバックする必要があります。
リレーショナルデータベーストランザクション 私のように、ほとんどの人は、リレーショナル データベース (MySQL、SQL Server、Oracle) について学習しているときに初めてトランザクションについて知りました。リレーショナル データベースでは、一連の操作が ACID 特性を満たす場合、その操作はトランザクションと呼ばれます。リレーショナル データベースの ACID 特性については、教科書やインターネット上に多くの情報が掲載されているため、ここでは簡単に紹介するだけにします。
送金の簡単な例を見てみましょう。ユーザー A がプレイヤー B に 100 元を送金します。これには 2 つの操作が含まれます。プレイヤー A のアカウントから 100 元が差し引かれ、プレイヤー B のアカウントに 100 元が追加されます。今すぐ
原子性は理解しやすいです。両方の操作が成功するか、どちらも実行されません (より正確には、効果はどちらも実行されないことと同じです)。ユーザー B のお金が増えていないのに、ユーザー A のお金が減ることはあり得ません。ユーザーはこれを許可しません。ユーザー A のお金が減っていないのに、ユーザー B のお金が増えるということはさらに不可能です。銀行は絶対にそんなことはしません。 一貫性について話すときは誰もがそれを理解しますが、深く調べてみると、まだ完全には理解できていません。インターネット上の ACID における一貫性の説明は非常に曖昧です。全員が、一貫した状態であるべきだと言っています。では、一貫した状態とは何でしょうか?たとえば、送金操作では、A がお金を差し引き、B がお金を追加し、AB のお金の合計は一定になります。これは ACID の一貫性に属するのでしょうか?そうは思わない。 Wiki Transaction_processing と Wiki: ACID は次のように説明されています。
上記の太字の黒い部分は、ACID の一貫性が整合性制約に違反していないことを意味することを示しています。整合性には、エンティティ整合性 (主属性が空ではない)、参照整合性 (外部キーが元のテーブルに存在する必要がある)、およびユーザー定義の整合性が含まれます。ユーザー定義の整合性。列の値が NULL でないか、列の値が一意であるか、列の値がブール式 (性別は 2 つの値のみを持つことができる、年齢は特定の範囲の整数であるなどのチェック ステートメント) を満たしているかどうかなど。たとえば、age smallint CHECK (age >=0 AND age <= 120) などです。データベースは、年齢の値が [0, 120] の範囲内にあることを保証します。この範囲内にない場合、更新操作は失敗し、トランザクションも失敗します。さらに、MySQL のカスケードとトリガーは、ユーザー定義の整合性制約です。 MongoDB 3.2 では、ドキュメント検証は、ドキュメントを挿入または更新するときにチェックされるユーザー定義の整合性制約です。ただし、ユーザーは、データが制約を満たしていない場合の動作を決定するために validationAction を設定できます。デフォルトはエラーであり、データ書き込み操作が拒否されることを意味します。 したがって、このトランザクション操作の前後のユーザー A と B のアカウントの合計は一定です。これは、データベースによって保証される一貫性ではなく、アプリケーション レベルでの一貫性です。アプリケーション レベルでの一貫性は、実際には原子性によって保証されます。 孤立について語るのは簡単ですが、実際にはその背後にあるものは非常に複雑です。データベースの分離は、ロックまたはマルチバージョン制御に依存します。簡単に言うと、UserA.account の初期値が 500 の場合、最初の命令 (つまり、100 を減算する) が実行された後、トランザクションがコミットされていないと、他のトランザクションはこの中間結果 (UserA.account の値は 400) を読み取ることができません。これはダーティ リード (Drity Read) を回避するためのもので、対応する分離レベルは READ_COMMITTED です。 SQL 標準では、次の 4 つの分離レベルが定義されています。
トランザクションの同時実行によって発生するダーティリード、非反復リード、ファントムリードの問題を解決する 異なるデータベースまたはストレージ エンジンは、デフォルトで異なる分離レベルをサポートします。たとえば、InnoDB ストレージ エンジンはデフォルトで REPEATABLE_READ をサポートしますが、Mongodb は READ_UNCOMMITTED のみをサポートします。 永続性では、トランザクションの実行中に発生する可能性のあるさまざまな例外を考慮する必要があります。取引のプロセスは次のとおりです。
分散トランザクション データの規模が大きくなると、単一のリレーショナル データベースの処理能力を超えてしまいます。このとき、リレーショナルデータの垂直分割やシャーディングが登場し、水平拡張(シャーディング)も当然サポートするNoSqlも登場します。さらに、大規模 Web サイトのサービス指向 (SOA) や、過去 2 年間で非常に人気が高まったマイクロサービスでは、サービスを分割して個別に展開することがよくあります。当然、独立したデータベースや異種データベースも使用されます。現時点では、ロックやログ記録など、トランザクションを保証するためにリレーショナル データベースで使用される手段は機能しません。もちろん、この記事ではデータベースだけでなく、分散ストレージ、メッセージ キュー、アトミック性と永続性を確保するために必要なロジックについても説明します。 分散トランザクションの最大の課題は CAP にあります。これは、「CAP 理論と MongoDB の一貫性と可用性に関する考察」という記事で詳しく説明されています。つまり、ネットワーク パーティション (P: Network Partition) が存在するため、ユーザーは一貫性 (C Consistency) と可用性 (A: Avaliable) の間でトレードオフを行う必要があります。強力な一貫性 (主にアプリケーション レベルでの強力な一貫性) を確保したい場合、ネットワークが分割されるとシステムは使用できなくなります。高可用性を確保したい場合は、最終的な一貫性を確保するために弱い一貫性のみを提供できます。以下で説明する分散トランザクションを実装するためのさまざまな方法とプロトコルでは、一貫性と可用性の間でトレードオフが必要になります。 2PC 分散トランザクションに関して、まず最初に思い浮かぶのは間違いなく 2 フェーズ コミット (2pc、2 フェーズ コミット プロトコル) です。 2pc は、非常に古典的な強力な一貫性を持つ集中型アトミック コミット プロトコルです。集中化とは、プロトコル内に、集中型コーディネーター ノード (コーディネーター) と N 個の参加者ノード (参加者、コホート) の 2 種類のノードがあることを意味します。 名前が示すように、2 フェーズ コミット プロトコルの各トランザクション送信は 2 つのフェーズに分かれています。
ウィキでは簡単なプロセスが説明されています: 上の図の下の行は、2 フェーズ コミット プロトコルもログに依存していることを示していることに注意してください。ストレージメディアに問題がない限り、2フェーズプロトコルは最終的に一貫した状態(成功またはロールバック)に到達できます。 次の図 (slideshare から) は、プロセス全体を詳細に説明しています。 Liu Jie の「分散原理入門」には非常に詳細なプロセス紹介があり、上の図と合わせて読むことができます。さらに、さまざまな異常な状況 (コーディネーター、参加者のクラッシュ、ネットワークのセグメント化によるタイムアウトなど) における 2 フェーズ プロトコルの動作状態についても説明します。ここでは 2PC の利点と欠点についてのみ説明します。
この記事では、2 フェーズ コミット プロトコルが分散システムに適さない理由について説明します。 システムの「水平」スケーリングの最大の敵。 2 フェーズ コミットに基づく分散トランザクションでは、トランザクションをコミットするときに複数のノード間で調整する必要があり、これによりトランザクションをコミットする時点が最大限に延期され、トランザクションの実行時間が客観的に長くなります。これにより、トランザクションが共有リソースにアクセスするときに競合やデッドロックが発生する可能性が高まります。データベース ノードが増加するにつれて、この傾向はますます深刻になり、データベース レベルでのシステムの水平スケーリングの「足かせ」になります。これが、多くのシャーディング システムが分散トランザクションを使用しない主な理由です。 それは本当にそうですね! 3PC 3 フェーズ コミット プロトコル (3pc Three-phase_commit_protocol) は、主に 2 フェーズ コミット プロトコルのブロッキング問題を解決するために使用されます。元の 2 段階から 3 段階に拡張され、タイムアウト メカニズムが追加されます。 3PC は異常な状況下での 2PC のブロッキング問題のみを解決しますが、1 回の送信で 6 つのメッセージが送信されるため、大きな遅延が発生します。プロセスの詳細な説明については、「分散トランザクション、2 フェーズ コミット プロトコル、および 3 フェーズ コミット プロトコルについて」の記事を参照してください。 TCC TCC は Try、Commit、Cancel の略です。 Alipayのプロモーションにより中国では広く知られています。 TCC は、強力な一貫性を確保しながら、システムのスケーラビリティと可用性を最大化します。 完全なビジネスにはサブビジネスの集合が含まれていると想定します。 Try 操作は、すべてのサブビジネス チェックを完了し、必要なビジネス リソースを予約し、他のトランザクションからの分離を実現します。 Confirm は、Try フェーズで予約されたビジネス リソースを使用して実際にビジネスを実行し、Confirm 操作は再試行をサポートするためにべき等性を満たします。キャンセル操作は、Try フェーズで予約されたビジネス リソースを解放し、これによってもべき等性が満たされます。 「完全なトランザクションは、一連のマイクロトランザクションの Try 操作で構成されます。すべての Try 操作が成功した場合、マイクロトランザクション フレームワークは最終的に Confirmation を統合し、そうでない場合は Cancel を統合して、従来の 2 フェーズ コミット プロトコル (2PC) に似た強力な一貫性を実現します。」 2PC プロトコルと比較して、TCC には次の特徴があります。
もちろん、TCC には比較的高い開発コストが必要であり、各サブビジネスには応答性の高い確認およびキャンセル操作、つまり対応する補償ロジックを実装する必要があります。 メッセージベースの分散トランザクション このタイプのトランザクション メカニズムは、分散トランザクションを複数のローカル トランザクションに分割します。ここでは、これらをマスター トランザクションとスレーブ トランザクションと呼びます。まず、マスター トランザクションがローカルでコミットされ、次にスレーブ トランザクションにメッセージを通じて通知されます。スレーブ トランザクションはメッセージから情報を取得し、それをローカルにコミットします。これは、最終的な一貫性のみを保証できる非同期トランザクション メカニズムであることがわかります。ただし、可用性は非常に高く、障害によってブロックされることはありません。さらに、メイントランザクションが最初にコミットされています。スレーブトランザクションをコミットできない場合、メイントランザクションをロールバックするのは依然として面倒です。したがって、このモードは、理論的には成功の確率が高いビジネス状況にのみ適用できます。つまり、スレーブ トランザクションのコミットの失敗は、故障によるものである可能性はありますが、論理エラーである可能性は低いです。 非同期メッセージに基づくトランザクション メカニズムには、ローカル メッセージ テーブルとトランザクション メッセージの 2 つの主なタイプがあります。両者の違いは、メイントランザクションをコミットしてメッセージを送信するという 2 つの操作のアトミック性をどのように確保するかです。 非同期メッセージングを使用して送金の例を実装する場合、操作は 4 つのステップに分かれます。ユーザー A がお金を差し引き、メッセージを送信し、ユーザー B がメッセージを受信し、ユーザー B がお金を差し引きます。最初の 2 つのステップでは、アトミック性を確保する必要があります。 A がお金の引き落としに成功したがメッセージを送信しなかった場合、ユーザー A は損失を被ります。メッセージは正常に送信されたが、お金が引き落とされなかった場合、ユーザー B は追加のお金を受け取りますが、銀行は絶対にこれを行いません。 ローカルメッセージテーブル ローカル メッセージ テーブルに基づくソリューションは、メッセージをローカル データベースに書き込み、メイン トランザクションの原子性とローカル トランザクションを介したメッセージ書き込みを保証することを意味します。たとえば、銀行振込の場合、疑似コードは次のようになります。
次に、プル モードまたはプッシュ モードを通じて、ビジネスからのメッセージを取得して実行します。プッシュ モードの場合、トランザクションからのメッセージをサブスクライブするには、通常、永続機能を備えたメッセージ キューが使用されます。プル モードの場合は、トランザクションからメッセージを定期的にプルして実行します。 MongoDB の記述はローカル メッセージ テーブルと非常に似ています。 WriteConcern が w:1 の場合、更新操作は、oplog とプライマリに書き込まれている限り、クライアントに返すことができます。セカンダリは oplog を非同期的にプルし、実行をローカルに記録します。 取引メッセージ: トランザクション メッセージングは、「トランザクション メッセージング」をサポートするメッセージ キューに依存します。基本的な考え方は、メッセージ ミドルウェアを使用して 2 フェーズ コミットを実装し、ローカル トランザクションとメッセージングを分散トランザクションに配置して、ローカル操作が成功し、外部メッセージングが成功するか、または両方が失敗するようにすることです。プロセスは次のとおりです。
詳細なプロセスを下の図に示します (画像ソースについては透かしを参照してください)。 ローカル メッセージ テーブル方式と比較すると、トランザクション メッセージはメッセージ ミドルウェアによってローカル トランザクションとメッセージの原子性が保証され、メッセージの保存にローカル データベースに依存しないことがわかります。ただし、「トランザクション メッセージ」を実装するメッセージ キューは比較的少なく、まだ十分に汎用的ではありません。 ローカル メッセージ テーブルであっても、トランザクション メッセージであっても、トランザクションが実行され、正確に 1 回だけ実行されるようにする必要があります。失敗した場合は再試行する必要がありますが、初回は再試行が不可能です。スレーブ トランザクションが最終的に失敗した場合、メイン ビジネスにロールバックするように通知する必要がありますか?ただし、この時点ではメイン トランザクションがコミットされているため、論理ロールバックを実現する唯一の方法は補償です。ただし、現在の時点はメイントランザクションの送信から一定の時間が経過しており、ロールバックが失敗する可能性もあります。したがって、最善の方法は、トランザクション ロジックが失敗しないようにすることです。障害が発生した場合は、ログに記録し、警告を発し、手動で介入します。 1個 「楽しさと利益のための分散システム」という記事で、1PC (1 フェーズ コミット) の概念を知りました。 2PC および 3PC と同等になります。 Wiki には公式エントリがなく、Google にも記事があまりありません。私の理解では、1PC は分散ストレージ システムのレプリケーション セット、つまりレプリケーション セット内の複数のノードのデータ送信に適用できます。一般的に言えば、これらのノードは同じデータを保存します。単一のノードが送信できる限り、理論的には他のノードも送信できるはずです。 「楽しさと利益のための分散システム」では、次のように説明されています。
つまり、分散ストレージで広く使用されている集中型レプリケーション セット プロトコル Primary Secondary の場合、一部のノードに障害が発生し、一部のノードが成功したときにロールバック操作が行われないと、不整合が発生する可能性があります。ただし、これらの分散ストレージ システムは、これらの不整合が一時的なものであることを保証するために最善を尽くし、再試行やその他の手段を通じて最終的な一貫性を確保します。 1PC の利点は、パフォーマンスが非常に優れており、物理的な障害が発生した場合にのみ不整合が発生することです。 たとえば、MongoDB では、更新操作はプライマリ ノードと oplog コレクションに書き込まれます。セカンダリ ノードは、プライマリ ノードの oplog コレクションから操作ログを取得して実行します。これは非同期プロセスです。セカンダリノードが障害により oplog の実行に失敗しても、プライマリノードのデータはロールバックされません。また、「Learning Distributed Systems with Questions: Centralized Replication Sets」では、データの信頼性を向上させるために (極端なケースでのデータ ロールバックを回避するために)、WriteConcern を w:Majority に設定する (シャードはプライマリ、セカンダリ、およびアービターから構成される) ことも説明されています。この時点でセカンダリの 1 つに障害が発生すると、書き込み操作は成功しません。したがって、タイムアウト期間に達すると、クライアントにエラー メッセージが返されます。ただし、この時点ではデータはプライマリ ノードに保持されており、ロールバックされません。この時点でセカンダリ ノードが再起動されると、ログはプライマリ ノードから取得され、実行されます。したがって、クライアントから返されたエラー メッセージに WriteResult.writeConcernError が含まれている場合は、慎重に処理する必要があります。 分散ファイルシステム GFS および haystack では、セカンダリ ノードに障害が発生した場合、単純で大まかな再試行が採用され、最終的に正しいデータを読み取ることができるようにいくつかのメカニズム (チェックサム、オフセット) が使用されます。 考察と結論 多くの場合、分散トランザクションではアトミック性のみを確保する必要があります。これにより、アプリケーション レベルでの一貫性も確保されますが、ローカル トランザクションは分離性と永続性を確保する責任を負います。 アトミック性は、分散されておらず、単一のプロセスと単一のスレッドのみである場合でも考慮する必要があるものです。これは C++ の RAII であり、Python のステートメントと、さまざまな言語の try...finally... です。プロセス間の非同期通信の場合、言語レベルのメカニズムを通じてアトミック性を保証することは困難です。 分散分野では、ネットワークやマシンの障害により再試行が必要になることが多いため、べき等性が非常に重要です。 電子商取引やオンラインチケット購入などの多くのシナリオでは、まず高可用性を保証する必要があり、強力な一貫性が採用される可能性は低いです。したがって、「処理中...」という中間ステータスも表示されます。バックグラウンドは非同期で処理される可能性があります。 12306 でチケットを購入された方は、注文が成功してから直前にチケットが発行できるかどうかまでに長い時間がかかることをご存知でしょう。 著者のビジネス分野では、結果的一貫性で十分である限り、強い一貫性を伴うシナリオは存在しません。 2PC、TCC、ローカル メッセージ テーブル、トランザクション メッセージのいずれの場合でも、上記のすべての方法では、追加のフレームワークまたはコンポーネントの導入が必要です。そのため、ビジネス補償がよく使用されます。たとえば、2 つのプロセスが関与する操作では、アトミック性とプロセス間 RPC 通信を確保する必要があります。この場合、通常はプロセス A が最初に実行され、次に RPC がプロセス B のインターフェースを呼び出します。プロセス B の戻り結果に基づいて、ロールバック (補償) するかどうかが決定されます。ただし、非同期 RPC、マルチスレッド、または 2 つ以上のプロセスの直列接続が関係する場合は、補正が不可能になるか、または困難になる場合があります。この場合、エラーログのみが記録され、手動調査が通知されます。そのため、取引補償は一般的で単純なビジネスにのみ適しており、普遍的なフレームワークを形成することは困難であり、あまり実用的ではありません。 私は、銀行振込のようなシナリオは強い一貫性を持たなければならないと常に信じてきました。その後、私はそのようなことに遭遇しました。友達に送金しました。私の側では送金が成功したと表示されましたが、友人はお金を受け取っていませんでした。時間がかかると思っていましたが、24時間経ってもまだ届きませんでした。自分で振込票を再度確認したところ、相手の銀行口座を間違って記入していたことが判明しました。したがって、転送操作は確実に強一貫性があるわけではなく、その実行方法に関する具体的な詳細はインターネット上で見つけることができないことがわかります。さらに困ったことに、送金が失敗し、私のお金が差し引かれ、友人もお金を受け取れなかったのです。しかし、何の連絡も無く、お金も返ってきませんでした。銀行に問い合わせた後にようやく返金されました。これは本当にひどい経験ですが、銀行がボスなので、私たちにできることは何もありません。 |
>>: ビッグデータを活用したいですか?パブリッククラウドまたはオンプレミス
データミドルプラットフォームの開発は、一般的に、データベース、データウェアハウス、ビッグデータプラッ...
最近、BI を使ったデータ分析システムに取り組んでいたため、検索エンジンとその検索パラメータのリスト...
SAP は、オーランドで開催された第 30 回 SAPPHIRE NOW® イベントで、消費者と企業...
ウェブサイトの記事はウェブサイト存続の基盤です。ウェブサイトの記事を通じてのみ、ユーザーのニーズを満...
私は、朱衛坤が素晴らしい人だと言うためにこの記事を書いているわけではありません。繰り返しますが、私は...
最近、大手企業が小紅樹の「芝生化ビジネス」に目を付けている。分析によると、大手インターネット企業は昨...
2018年7月18日、第5回Dynatrace Perform Greater Chinaユーザーカ...
Rap "Indescribable" ノード、私は個人的にこの会社とあまり関わ...
ウェブサイトの構造は、その性質に応じて論理構造と物理構造に分けられ、ウェブサイト内のページ間の階層関...
何らかの理由で、元のサーバースポンサーがネットワークケーブルを抜いてしまい、ウェブサイトにアクセスで...
数日前、 Pinduoduo は2019 年通期業績報告書を発表しました。報告書によると、Pindu...
10 月 27 日から 11 月 2 日まで、Fastcomet はハロウィーン プロモーションを開...
9 月 21 日のニュース: Vultr は、WeChat 決済方法に正式に接続したことを発表しまし...
BandwagonHost 香港 VPS は在庫切れです。中国本土のように高速 (登録済みのマシンと...
みなさんこんにちは。私はMuzi Chengzhouです。 SEO に関しては、ウェブマスターが毎日...