マイクロサービスで最も受け入れられているトランザクション戦略は何ですか


80

マイクロサービスを備えたシステムで発生した主要な問題の1つは、トランザクションが異なるサービスにまたがる場合のトランザクションの動作方法です。独自のアーキテクチャ内では、これを解決するために分散トランザクションを使用してきましたが、独自の問題があります。特にこれまでのところ、デッドロックは苦痛です。

別のオプションは、システム内のフローを認識し、システム全体にまたがるバックグラウンドプロセスとしてロールバックを処理する、カスタムメイドのトランザクションマネージャーのようです(他のサービスにロールバックするよう指示します)ダウンしている場合は、後で通知します)。

別の受け入れられるオプションはありますか?これらの両方に欠点があるようです。最初のものはデッドロックやその他の問題を引き起こし、2番目のものはデータの不整合を引き起こす可能性があります。より良いオプションはありますか?


確かに、これらのマイクロサービスは多くのクライアントによって同時に使用されますか、それとも一度に1つだけですか?
マルセル

2
マーセル、この問題にとらわれない質問をしようとしていました。しかし、その両方を実行するのに十分な大きさのシステムを使用しており、両方をサポートするアーキテクチャが必要だと仮定しましょう。
クリストフ

4
これは言葉では言い表せませんが、非常に重要な質問です。ほとんどの人は「トランザクション」について考えるとき、トランザクションの原子性、分離、または耐久性の側面ではなく、ほぼ排他的に一貫性について考えます。質問は、実際にどのようにmicroservicesアーキテクチャ与えられたACID準拠のシステムを作成します」記載すべき唯一の実装の一貫性はなくACIDの残りの部分は本当に便利ではありませんあなたの場合を除き不良データのように。。。
ジェフ・フィッシャー

10
私の質問をいつでも変更して、「言葉遣いが粗悪」にならないようにすることができます、ジェフフィッシャー。
クリストフ

この質問と議論は、SOに関するこの他の質問と密接に関連しています。
パウロMerson

回答:


42

通常のアプローチは、それらのマイクロサービスを可能な限り分離することです-それらを単一のユニットとして扱います。その後、トランザクション全体をサービスのコンテキストで開発できます(つまり、通常のDBトランザクションの一部ではなく、サービスの内部にDBトランザクションを保持できます)。

トランザクションがどのように発生し、サービスにとって意味があるかを考えて、元の操作を元に戻すロールバックメカニズム、または実際にコミットするように指示されるまで元の操作を予約する2フェーズコミットシステムを実装できます。もちろん、これらの両方のシステムは、独自のシステムを実装していることを意味しますが、その後、すでにマイクロサービスを実装しています。

金融サービスは常にこの種のことを行います-私の銀行からあなたの銀行にお金を移動したい場合、あなたがDBで持っているような単一のトランザクションはありません。どちらの銀行がどのシステムを実行しているかわからないため、それぞれをマイクロサービスのように効果的に扱う必要があります。この場合、私の銀行は私のお金を私の口座から持ち株口座に移動し、あなたの銀行にいくらかのお金があることを伝えます。その送信が失敗した場合、私の銀行は私の口座に送ろうとしたお金を払い戻します。


5
払い戻しが失敗することはないと想定しています。
サンジーブクマールダンギ

9
@SanjeevKumarDangi可能性は低く、たとえ失敗しても、保有口座と実際の口座は銀行の管理下にあるため、簡単に処理できます。
gldraphael

30

標準的な知恵は、トランザクションがマイクロサービスの境界を越えないようにすることだと思います。特定のデータセットが別のデータセットとアトミックに整合する必要がある場合、これら2つのものは一緒になります。

これは、完全に設計するまでシステムをサービスに分割することが非常に難しい理由の1つです。それは現代世界ではおそらくそれを書いたことを意味します...


37
このようなアプローチは、最終的にすべてのマイクロサービスを単一のモノリシックアプリケーションにマージすることにつながる可能性があります。
スラバフォーミンII

4
これが正しいアプローチです。実際には単純です。サービス間でトランザクションが必要な場合、サービスは間違っています。それらを再設計してください。@SlavaFominIIの言うことは、マイクロサービスシステムの設計方法がわからない場合にのみ当てはまります。マイクロサービスのアプローチと戦っている場合、やらないでください。モノリスは、悪いマイクロサービスの設計よりも優れています。適切なサービスの境界が見つかった場合にのみ、モノリスをサービスに分割する必要があります。それ以外の場合、マイクロサービスを使用することは適切なアーキテクチャの選択ではなく、誇大宣伝に従っています。
フランチェスクカステル

@FrancescCastellsサービスが他のサービス間のトランザクションを本当に必要とする場合、境界付けられたコンテキストを無視し、最終的に単一のトランザクションとしてサービスをモデル化する必要があるということですか?D:D:私はまだそれが....ナイーブに聞こえる場合ので、私の質問はご容赦読んで、microservicesで初心者です
ビルボ・バギンズ

@BilboBaggins境界のあるコンテキストを無視することの反対です。定義により、トランザクションは境界コンテキスト内で発生し、複数のトランザクション間では発生しません。したがって、境界付けられたコンテキストとともにマイクロサービスを正しく設計する場合、トランザクションは複数のサービスにまたがってはなりません。しばらくの間、必要なのはトランザクションではなく、物事が正しく行われない場合の最終的な一貫性の適切な処理と適切な補正アクションです。
フランチェスクカステル

私はあなたの意見を得ていませんが、チャットでこれについて議論できる可能性はありますか?
ビルボバギンズ

17

アプリケーションの一貫性が強力な要件である場合、マイクロサービスがより良いアプローチであるかどうかを自問する必要があると思います。マーティン・ファウラーが言うように

マイクロサービスは、分散データ管理に対する称賛に値する主張のため、結果整合性の問題をもたらします。モノリスを使用すると、1つのトランザクションで多数のものをまとめて更新できます。マイクロサービスを更新するには複数のリソースが必要であり、分散トランザクションは(正当な理由で)眉をひそめています。そのため、開発者は一貫性の問題を認識し、コードが後悔する前に同期が取れていないことを検出する方法を見つける必要があります。

しかし、おそらくあなたの場合、可用性の位置で一貫性を犠牲にすることができます

多くの場合、ビジネスは可用性を高く評価するため、ビジネスプロセスは、あなたが考えるよりも不整合に寛容です。

ただし、マイクロサービスに分散トランザクションの戦略があるかどうかも自問しますが、コストが高すぎる可能性があります。Martin FowlerとCAP定理の常に優れた記事で2セントを差し上げたいと思いました。


1
分散ビジネストランザクションでは、ワークフローエンジンや慎重に設計されたキューなどのオーケストレーションレイヤーを追加することで、一貫性を確保することがあります。その層は、コミットとロールバックの2つのフェーズすべてを処理し、マイクロサービスが特定のビジネスロジックに集中できるようにします。しかし、CAPに戻って、可用性と一貫性に投資すると、パフォーマンスが犠牲になります。マイクロサービスは、OOPでビジネスを構成する多くの分離クラスと比較してどうですか?
グレッグ

マイクロサービス経由でRAFTを使用して、一貫性に対処することができますFWIW
f0ster

1
+1。データのすべての「プル」ビューをマテリアライズドビューとして実装できる場合、マイクロサービスコンテキストでトランザクションが必要なのはなぜだろうとよく思います。たとえば、1つのマイクロサービスが1つのアカウントから借方を実装し、別のアカウントから別のクレジットを借方に実装する場合、残高のビューはクレジットと借方のペアのみを考慮します。
センチネル

16

ここでの回答の少なくとも1つだけでなく、Webの他の場所でも提案されているように、2つのエンティティ間の整合性が必要な場合、通常のトランザクション内でエンティティを永続化する1つのマイクロサービスを設計することができます。

ただし、同時に、エンティティが実際に同じマイクロサービスに属していない場合もあります。たとえば、販売レコードと注文レコード(販売を遂行するために何かを注文する場合)です。このような場合、2つのマイクロサービス間の一貫性を確保する方法が必要になる場合があります。

従来、分散トランザクションが使用されてきましたが、私の経験では、ロックが問題になるサイズに拡張するまでうまく機能します。ロックを緩和して、状態の変更を使用して実際に関連するリソース(販売されているアイテムなど)のみが「ロック」されるようにすることができますが、すべてのデータベースがあなたのためにそれを処理するのではなく、自分でこれを行うためのロジック。

私は、この複雑な問題を処理するために独自のトランザクションフレームワークを構築するルートを進めてきた企業と協力しましたが、高価で成熟するのに時間がかかるため、お勧めしません。

一貫性を保つシステムにボルトで固定できる製品があります。ビジネスプロセスエンジンは良い例であり、通常は最終的に整合性を処理し、補償を使用します。他の製品も同様に機能します。通常、クライアントの近くにソフトウェアの層ができます。この層は、一貫性とトランザクションを処理し、実際のビジネス処理を行うための(マイクロ)サービスを呼び出します。そのような製品の1つに、Java EEソリューションで使用できる汎用JCAコネクターがあります(透明性のために:私は著者です)。ここで提起された問題の詳細と詳細な議論については、http://blog.maxant.co.uk/pebble/2015/08/04/1438716480000.htmlを参照してください。

トランザクションと一貫性を処理する別の方法は、マイクロサービスへの呼び出しを、メッセージキューのようなトランザクション的なものへの呼び出しにラップすることです。上記の販売記録/注文記録の例を見てみましょう-販売マイクロサービスにメッセージを注文システムに送信させるだけで、データベースに販売を書き込む同じトランザクションでコミットされます。その結果、非常に優れた拡張性を備えた非同期ソリューションが実現します。Webソケットなどの技術を使用すると、非同期ソリューションのスケールアップに関連することが多いブロッキングの問題を回避することもできます。このようなパターンのその他のアイデアについては、別の記事http://blog.maxant.co.uk/pebble/2015/08/11/1439322480000.htmlを参照してください。

どちらのソリューションを選択しても、一貫性が必要なものをシステムのごく一部だけが書き込むことに注意することが重要です。ほとんどのアクセスは読み取り専用です。そのため、トランザクション管理はシステムの関連部分にのみ構築し、適切に拡張できるようにします。


その間、プロセスを非同期プロセスに変えることを真剣に検討する必要があると思います。非同期プロセスでは、プロセスの各ステップが完全にトランザクション処理されます。詳細はこちらをご覧ください:blog.maxant.co.uk/pebble/2018/02/18/1518974314273.html
Ant Kutschera

「上記の販売記録/注文記録の例を作成します。販売マイクロサービスに注文システムにメッセージを送信させるだけで、データベースに販売を書き込む同じトランザクションでコミットされます。」あなたが提案しているのが基本的に分散トランザクションであるかどうかはわかりませんが、それはこの場合のロールバックシナリオをどのように扱いますか?たとえば、メッセージはメッセージキューにコミットされますが、DB側でロールバックされました。
sactiw

@sactiwは、2フェーズコミットを念頭に置いているかどうかわかりませんが、今はそれを避け、代わりにビジネスデータを書き込み、マイクロサービスDBへの1つのトランザクションでメッセージをキューに追加する必要があるという事実。「事実」、つまり「コマンド」は、自動再試行メカニズムを使用して、トランザクションがコミットされた後に非同期で処理されます。例については、2018年3月10日のブログ記事を参照してください。実装が容易であるため、可能であれば、フォワード戦略を支持してロールバックや補償を避けてください。
アントクッチェラ

1

マイクロサービスでは、差分間の一貫性を実現する3つの方法があります。サービス:

  1. オーケストレーション-サービス全体でトランザクションとロールバックを管理する1つのプロセス。

  2. 振り付け-サービスは相互にメッセージを渡し、最終的に一貫した状態に達します。

  3. ハイブリッド-上記2つを混合します。

完全な読書のためにリンクに行きなさい:https : //medium.com/capital-one-developers/microservices-when-to-react-vs-orchestrate-c6b18308a14c


1

私が慣れている以上に妥協する多くのソリューションがあります。確かに、異なる銀行間でお金を移動するなど、ユースケースが複雑な場合、より快適な代替手段は不可能かもしれません。しかし、マイクロサービスの使用がデータベーストランザクションと干渉する一般的なシナリオでできることを見てみましょう。

オプション1:可能な限りトランザクションの必要性を回避する

明白で前に言及しましたが、管理できれば理想的です。コンポーネントは実際に同じマイクロサービスに属していましたか?または、トランザクションが不要になるようにシステムを再設計できますか?おそらく、非トランザクション性を受け入れることが最も手頃な犠牲です。

オプション2:キューを使用する

他のサービスが私たちがやりたいことに成功するのに十分な確実性がある場合、何らかの形式のキューを介してそれを呼び出すことができます。キューに入れられたアイテムは後までピックアップされませんが、アイテムがキューに入れられていることを確認できます。

たとえば、単一のトランザクションとしてエンティティを挿入し、電子メールを送信するとします。メールサーバーを呼び出す代わりに、テーブル内の電子メールをキューに入れます。

Begin transaction
Insert entity
Insert e-mail
Commit transaction

明らかな欠点は、複数のマイクロサービスが同じテーブルにアクセスする必要があることです。

オプション3:トランザクションを完了する直前に、外部作業を最後に行う

このアプローチは、トランザクションのコミットが失敗する可能性が非常に低いという前提に基づいています。

Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction

クエリが失敗した場合、外部呼び出しはまだ行われていません。外部呼び出しが失敗した場合、トランザクションはコミットされません。

このアプローチには、1つの外部呼び出ししか作成できないという制限があり、最後に実行する必要があります(つまり、クエリでその結果を使用することはできません)。

オプション4:保留状態の物を作成する

ここに投稿されているように、複数のマイクロサービスに異なるコンポーネントを作成させ、それぞれを非トランザクションで保留状態にすることができます。

検証は実行されますが、最終的な状態では何も作成されません。すべてが正常に作成されると、各コンポーネントがアクティブになります。通常、この操作は非常に単純で、何か問題が発生する確率は非常に小さいため、非トランザクションでアクティベーションを行うことを好む場合もあります。

最大の欠点は、おそらく保留中のアイテムの存在を考慮する必要があることです。選択クエリでは、保留中のデータを含めるかどうかを考慮する必要があります。ほとんどは無視する必要があります。そして、更新はまったく別の話です。

オプション5:マイクロサービスにクエリを共有させる

他のオプションはありませんか?次に、非正統派を取得しましょう。

会社によっては、これは受け入れられない場合があります。私は知っています。これは非正統的です。受け入れられない場合は、別のルートに進みます。しかし、これがあなたの状況に合っていれば、問題を簡単かつ強力に解決します。それは、最も受け入れられる妥協案にすぎないかもしれません。

複数のマイクロサービスからのクエリを単純な単一のデータベーストランザクションに変換する方法があります。

クエリを実行するのではなく、返します。

Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction

ネットワークに関しては、各マイクロサービスが各データベースにアクセスできる必要があります。将来のスケーリングに関しても、このことに留意してください。

トランザクションに関係するデータベースが同じサーバー上にある場合、これは通常のトランザクションになります。それらが異なるサーバー上にある場合、それは分散トランザクションになります。コードは関係なく同じです。

接続タイプ、パラメータ、接続文字列を含むクエリを受け取ります。フローを読みやすい状態に保ちながら、きちんとした実行可能なCommandクラスにまとめることができます。マイクロサービス呼び出しは、トランザクションの一部として実行するCommandになります。

接続文字列は、発信元のマイクロサービスが提供するものであるため、すべての意図および目的に対して、クエリは依然としてそのマイクロサービスによって実行されていると見なされます。クライアントマイクロサービスを介して物理的にルーティングするだけです。それは違いがありますか?それで、別のクエリと同じトランザクションに入れることができます。

妥協が容認できる場合、このアプローチにより、マイクロサービスアーキテクチャでのモノリスアプリケーションの単純なトランザクション性が得られます。


0

私は、問題空間を分解することから始めます - あなたのサービス境界を特定します。適切に行われれば、サービス間でトランザクションを行う必要はありません。

さまざまなサービスには、独自のデータ、動作、動機付けの力、政府、ビジネスルールなどがあります。まず、企業が持つ高レベルの機能を一覧表示することから始めてください。たとえば、マーケティング、販売、経理、サポート。別の出発点は組織構造ですが、警告があることに注意してください-いくつかの理由(たとえば、政治的)のために、それは最適なビジネス分解スキームではないかもしれません。より厳密なアプローチは、バリューチェーン分析です。サービスには人を含めることもできますが、厳密にはソフトウェアではありません。サービスは、イベントを介して互いに通信する必要があります

次のステップは、これらのサービスを切り分けることです。その結果、比較的独立した集計を取得できます。それらは一貫性の単位を表します。言い換えれば、内部構造は一貫性があり、ACIDである必要があります。集合体は、イベントを介して互いに通信します。

ドメインが最初に一貫性を要求していると思われる場合は、もう一度考えてください。これを念頭に置いて構築された大規模でミッションクリティカルなシステムはありません。それらはすべて分散され、最終的には一貫しています。パット・ヘランドの古典的な論文をチェックしてください。

分散システムを構築する方法に関する実用的なヒントを次に示します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.