web-dev-qa-db-ja.com

二相コミット

ほとんどの人は2PC(2フェーズコミットプロトコル)とは何か、Javaまたはほとんどの現代言語で使用する方法を知っています。基本的には、トランザクションが2つ以上のDBがある場合に同期します。

2つの異なる場所で2PCを使用する2つのDB(AとB)があるとします。 AとBがトランザクションをコミットする準備ができる前に、両方のDBがトランザクションマネージャーに報告し、コミットする準備ができたことを報告します。したがって、トランザクションマネージャが確認されると、AとBに信号を送り、先に進むように伝えます。

私の質問は次のとおりです。Aがシグナルを受信し、トランザクションをコミットしたとします。すべてが完了すると、Bは同じことをしようとしますが、誰かが電源ケーブルを抜いて、サーバー全体がシャットダウンします。 Bがオンラインに戻ったとき、Bは何をしますか?そして、Bはどうやってそれをしますか?

Aはコミットされますが、Bはコミットされず、2PCを使用していることを思い出してください(2PCの設計は機能しなくなりますよね?)

53
dongshengcn

二相コミット時

2フェーズコミットは、分散トランザクションが失敗しないことを保証するものではありませんが、TMがそれを認識せずに黙って失敗しないことを保証します。

Bがトランザクションをコミットする準備ができていると報告するには、Bはトランザクションを永続ストレージに保持する必要があります(つまり、Bはすべての状況でトランザクションがコミットできることを保証できなければなりません)。この状況では、Bはトランザクションを永続化しましたが、トランザクションマネージャーはBがコミットを完了したことを確認するメッセージをBからまだ受信していません。

トランザクションマネージャは、Bがオンラインに戻ったときにBを再度ポーリングし、トランザクションをコミットするように依頼します。 Bがすでにトランザクションをコミットしている場合、トランザクションをコミット済みとして報告します。 Bがまだトランザクションをコミットしていない場合は、Bが既に永続化しているためコミットし、トランザクションをコミットできる状態にあります。

この状況でBが失敗するためには、データまたはログエントリを失う致命的な障害が発生する必要があります。トランザクションマネージャは、Bがコミットの成功を報告しなかったことを依然として認識しています。1

実際には、Bがトランザクションをコミットできなくなった場合、Bを奪った災害がデータ損失を引き起こしたことを意味し、TMが認識していないTxIDをコミットするように要求したときにBはエラーを報告しますコミット可能な状態にあるとは思わなかった。

したがって、2フェーズコミットは壊滅的な障害の発生を防止しませんが、障害が気付かれることを防ぎます。このシナリオでは、Bがコミットできない場合、トランザクションマネージャーはアプリケーションにエラーを報告します。

アプリケーションは依然としてエラーから回復できる必要がありますが、アプリケーションが不整合状態を認識しない限り、トランザクションがサイレントに失敗することはありません。

セマンティクス

  • フェーズ1でリソースマネージャーまたはネットワークがダウンした場合、トランザクションマネージャーは致命的なエラー(リソースマネージャーに接続できない)を検出し、サブトランザクションを失敗としてマークします。ネットワークが復旧すると、参加しているすべてのリソースマネージャーでトランザクションが中止されます。

  • フェーズ2でリソースマネージャーまたはネットワークがダウンした場合、トランザクションマネージャーは、リソースマネージャーが復旧するまでポーリングを続けます。リソースマネージャーに再接続すると、RMにトランザクションをコミットするように指示します。 RMが「Unknown TxID」の行に沿ってエラーを返す場合、TMはRMにデータ損失の問題があることを認識します。

  • TMがフェーズ1でダウンした場合、クライアントは、タイムアウトするか、ネットワーク接続の切断によるエラーを受信しない限り、TMが復旧するまでブロックします。この場合、クライアントはエラーを認識し、再試行するか、中止自体を開始できます。

  • TMがフェーズ2でダウンした場合、TMが復旧するまでクライアントをブロックします。トランザクションはコミット可能として既に報告されており、クライアントに致命的なエラーが表示されることはありませんが、TMが復旧するまでブロックされる可能性があります。 TMは、トランザクションをコミットされていない状態のままにし、RMをポーリングして、バックアップがコミットされたときにコミットします。

リソースマネージャーでのコミット後のデータ損失イベントは、トランザクションマネージャーによって処理されず、RMの復元力の関数です。

2フェーズコミットはフォールトトレランスを保証しません-フォールトトレランスに対処するプロトコルの例については Paxos を参照してください-ただし、分散トランザクションの部分的な失敗が気付かれることはありません。

  1. この種の障害は、以前にコミットされたトランザクションからデータを失う可能性があることに注意してください。 2フェーズコミットは、リソースマネージャーがデータを失ったり破損したりすることや、DRプロシージャが失敗しないことを保証しません。

3フェーズコミットの方がはるかに優れたアプローチだと思います。残念ながら、そのような技術を実装している人はいません。

http://the-paper-trail.org/blog/consensus-protocols-three-phase-commit/

上記の記事の重要な部分は次のとおりです。

2PCの根本的な難点は、コーディネーターがコミットの決定を下し、一部のレプリカと通信すると、レプリカはすぐに進み、他のすべてのレプリカがメッセージを受け取ったかどうかを確認せずにコミットステートメントに基づいて動作することです。その後、コミットしたレプリカがコーディネーターとともにクラッシュした場合、システムはトランザクションの結果が何であるかを知る方法がありません(コーディネーターとメッセージを受け取ったレプリカだけが確実に知っているため)。トランザクションはクラッシュしたレプリカですでにコミットされている可能性があるため、プロトコルは悲観的に中止できません。トランザクションには取り消しが不可能な副作用があった可能性があるためです。同様に、元の投票が中止された可能性があるため、プロトコルはトランザクションを楽観的にコミットすることはできません。

この問題は、ほとんどの場合、2PCに余分なフェーズを追加することで回避され、3フェーズコミットプロトコルを提供します。アイデアはとてもシンプルです。 2PCの2番目のフェーズである「コミット」を2つのサブフェーズに分割します。最初は「コミットの準備」フェーズです。コーディネーターは、第1フェーズで全会一致の「はい」票を受け取ったときに、すべてのレプリカにこのメッセージを送信します。このメッセージを受信すると、レプリカは、必要なロックなどを取得することでトランザクションをコミットできる状態になりますが、後で元に戻すことができない作業を行わないことが決定的に重要です。その後、コーディネーターに返信して、「コミットの準備」メッセージを受信したことを伝えます。

このフェーズの目的は、投票の結果をすべてのレプリカに伝達し、どのレプリカが死んだとしてもプロトコルの状態を回復できるようにすることです。

プロトコルの最後のフェーズは、2PCの元の「コミットまたは中止」フェーズとほぼ同じことを行います。コーディネーターがすべてのレプリカから「コミットの準備」メッセージの配信の確認を受け取った場合、トランザクションのコミットを続行しても安全です。ただし、配信が確認されない場合、コーディネーターは、クラッシュした場合にプロトコル状態が回復することを保証できません(固定数fの障害を許容している場合、コーディネーターはf + 1を受信すると先に進むことができます確認)。この場合、コーディネーターはトランザクションを中止します。

コーディネーターが任意の時点でクラッシュした場合、回復ノードがトランザクションを引き継ぎ、残りのレプリカから状態を照会できます。トランザクションをコミットしたレプリカがクラッシュした場合、他のすべてのレプリカが「コミットする準備」メッセージを受信したことがわかります(そうでない場合、コーディネーターはコミットフェーズに移動しませんでした)。したがって、リカバリノードはトランザクションをコミットできたことを判断し、プロトコルを安全に暗号化して結論に至りました。レプリカが「コミットの準備」を受け取っていないことを回復ノードに報告した場合、回復ノードはトランザクションがどのレプリカでもコミットされていないことを知るため、悲観的にプロトコルを中止または再実行できます最初から。

3PCはすべての問題を解決しますか?かなりではありませんが、間近に迫っています。ネットワークパーティションの場合は、むしろ車輪が外れます。「コミットの準備」を受け取ったレプリカはすべてパーティションの一方の側にあり、そうでないレプリカはもう一方の側にあると想像してください。その後、両方のパーティションは、それぞれトランザクションをコミットまたはアボートするリカバリノードを使用して続行され、ネットワークがマージされると、システムは一貫性のない状態になります。したがって、2PCと同様に、3PCには潜在的に安全でない実行がありますが、常に進歩し、したがってその活性プロパティを満たします。単一ノードの障害で3PCがブロックされないという事実は、低レイテンシよりも高可用性が重要なサービスにとって非常に魅力的です。

3
Makis Gerakas

あなたのシナリオは、すべての努力にもかかわらず、物事が最終的にうまくいかない可能性がある唯一のものではありません。 AとBの両方がTMに「コミットする準備ができている」と報告し、次に誰かがTMとBの間の回線を切断したと仮定します。 TMが再接続するまで永遠に待機し続けることはできません(トランザクションに関係する自身のリソースは、明らかな理由により、待機時間全体を通してロック/アクセス不可のままでなければなりません)。したがって、Bがそれ自体の味を待つ時間が長すぎると、いわゆる「ヒューリスティックな決定」が必要になります。つまり、TMから独立してコミットまたはロールバックすることを決定します。これは、よくわからないのですが、それは実際には問題ではありません。このようなヒューリスティックな決定は、TMが行う実際のコミット決定から逸脱する可能性があることは明らかです。

3
Erwin Smout