負荷の高いシステムでのデータの一貫性

発行



ほとんどの情報システムでは、継続的にデータを保存する必要があります。 中小規模の負荷を伴うほとんどのシステムでは、この機能はリレーショナルDBMSによって実行されます。リレーショナルDBMSの紛れもない利点は、データの一貫性の保証です。



データの一貫性とは何かを説明する古典的な例-ある口座から別の口座に資金を移動する操作。 1つのアカウントの残高を変更する操作が既に完了し、もう1つのアカウントにまだ時間がなかった時点で、障害が発生する可能性があります。 その後、資金は1つの口座から引き落とされますが、別の口座には入金されません。 このシステムデータの状態は不整合と呼ばれ、おそらく、これがどのような結果をもたらすかを説明する必要はありません。 リレーショナルDBMSは、常にデータの一貫性を保証するトランザクションメカニズムを提供します。 トランザクションは、1つの一貫性のある状態を別の一貫性のある状態に転送する有限の操作セットです。 いずれかのステップでエラーが発生した場合、DBMSは以前に実行されたすべての操作をキャンセルし、データを元の合意された状態に戻します。 つまり、すべての操作が実行されるか、単一の操作ではありません。



大規模システムについては、作業負荷が大きすぎるため、単一のデータベースを使用することは常に不可能です。 このような場合、各システムモジュール(サービス)には独自の個別のデータベースが提供されます。 この場合、このようなクラスターアーキテクチャのデータの一貫性を確保する方法が問題になります。



データの一貫性の解決



1つのソリューションは分散トランザクションです。 まず、クラスターのすべてのノードが操作が可能であることに同意する必要があり、次に変更がすべてのノードにコミットされます。 ノードには共通のストレージデバイスがないため、共通の意見を得る唯一の方法は、分散コンセンサスプロトコルを使用して同意することです。



グローバルトランザクションをキャプチャするための簡単なプロトコルは、2フェーズコミット(2PC)です。 トランザクションを実行するノードはコーディネーターと見なされます。 準備フェーズ(準備)で、コーディネーターはコミットトランザクションについて他のノードに通知し、コミットの準備ができていることを確認するまで待機します。 少なくとも1つのノードの準備ができていない場合、トランザクションは中断します。 コミット段階では、コーディネーターはすべてのノードにトランザクションをコミットする決定を通知します。 すべてが正常であるという全員からの確認を受信すると、コーディネーターはトランザクションもコミットします。



画像



図1-2フェーズコミットの一般的なスキーム



このプロトコルは最小限のメッセージを回避しますが、堅牢ではありません。 たとえば、準備フェーズ後にコーディネーターに障害が発生した場合、残りのノードには、トランザクションをコミットするかキャンセルするかに関する情報がありません(障害が修正されるまで待機する必要があります)。 2PC(および3PCなどの他の分散トランザクションプロトコル)のもう1つの重大な欠点は、クラスターノードの数が増えると、2フェーズコミットのパフォーマンスが低下することです。



画像



図2-DBMSクラスター内のサーバー数に対する2ベースコミットの速度の依存性



さらに、分散トランザクションアプローチには制限があります。システムのすべてのモジュールは同じDBMSを使用する必要がありますが、これは必ずしも便利ではありません。



別のオプションは、さまざまなデータベース(サービス用)を単一のデータベースとして使用できるようにするメカニズムを提供することです(分散データベースのデータ整合性の問題を解決するため)。 同時に、分散システムのトランザクションに類似したものが必要です(「ビジネストランザクション」)。



通常のトランザクションおよび2フェーズコミットでは、すべての操作はトランザクションメカニズムによって制御され(ロックを使用)、これはすべての操作をロールバックする機能を提供するために行われます(悲観的なアプローチ-すべての操作が潜在的に障害を引き起こすと考えます)。 これがシステムのボトルネックです。 別の方法は、いわゆる楽観的なアプローチです。ほとんどの操作が正常に完了すると考えています。 また、障害が発生した場合に追加のアクションを実行します。 つまり ほとんどの操作のコストを削減し、生産性の向上につながります。



佐賀とは何ですか?



マイクロサービスアーキテクチャのトランザクションの代替手段は佐賀です。 佐賀(saga)は、システムのさまざまなモジュール(サービス)によって実行される一連のステップです。 サーガサービスも必要です。サーガサービスは、全体として操作(ビジネストランザクション)を担当します。 ステップは、イベントグラフを介してリンクされます。 サガが完了した後、システムは、合意された状態から別の状態に移行する(実行が成功した場合)か、以前の合意した状態に戻る(キャンセルの場合)必要があります。



このようなビジネストランザクションのリターンまたはロールバックを実装する方法は? これを行うために、サガはステップのキャンセルのメカニズム(代償アクション)を使用します。 たとえば、ステップの1つは成功しました(たとえば、エントリがユーザーデータベーステーブルに追加されました)が、次のステップの1つが失敗したため、サガ全体をキャンセルする必要があります。 次に、同じサービスがコマンドを受け取ります-アクションをキャンセルします。 ただし、サービスDBMSでは、ローカルトランザクションは既に完了しており、ユーザーレコードが追加されています。 次に、前の状態に戻るために、サービスは補正アクションを実行する必要があります(この例では、レコードを削除します)。 ステップをキャンセルすると、サガのフレームワーク内で原子性(「すべてまたは何も」)を実装できます。すべてのステップが実行または補正されます。



画像



図3-佐賀の仕事のメカニズムと代償効果の性質



図3では、サガのステップはT1 ... T4として指定され、アクションを補正します:C1 ... C4。

Sagasは、ステップのi等性をサポートします(繰り返される繰り返しが1つのアクションに相当するアクション)。 サグアプローチは、任意の手順を繰り返す機会を提供します(たとえば、正常終了時に応答を受信しなかった場合)。 また、dem等性を使用すると、ノードでデータが失われたときに状態を復元できます(障害および回復)。 ステップを実行するとき、各サービスは(べき等キーによって)このステップを既に実行したかどうか(実行しない場合は実行する、スキップする)を決定する必要があります。 アクションを補正するために、べき等キーを追加して操作を繰り返すこともできます(永続性/安定性を確保する)。



まとめ



ACIDトランザクションシステムの4つの要件(原子性、一貫性、分離、安定性)のうち、サグメカニズムにより、3つを実装できます(すべて分離を除く)。 分離がないと、異常(「ダーティリード」、「繰り返し不可のリード」、異なるビジネストランザクション間の変更の書き換えなど)につながる可能性があります。 このような状況を克服するには、追加のメカニズム、たとえば可変オブジェクトのバージョン管理を使用する必要があります。



Sagasを使用すると、次のタスクを解決できます。





範囲と適用例



Sagasは、多くの要求があるシステムでよく使用されます。 たとえば、人気のあるメールサービス、ソーシャルネットワーク。 ただし、このアプローチでは、小規模のプロジェクトで用途が見つかる場合があります。



当社は、大企業向けの会計システムの開発経験があり、数十人のユーザー向けに設計されており、すべてのデータが1つのリレーショナルDBMSに保存されていました。 計画された作業の自動計算中に問題が発生しました。場合によっては、計算が非常に大きく、DBMSテーブルに数百万のレコードを挿入する必要があり、DBMSを大幅にロードし、システム全体の動作を遅くしました。



解決策が見つかりました-作業計算ロジックを、作業自体と関連オブジェクトを保存するための独自のDBMSを備えた別のサービスに入れること。 データの一貫性は、サガによって保証されました。 計算が失敗した場合、アプリケーションのメインモジュールは論理計算操作をキャンセルするコマンドを受け取りました。



佐賀対応ライブラリ



このアプリケーションは.Netで開発されました。このテクノロジーには、サガをサポートする複数のライブラリサービスマネージャーがいます。 NServiceBus、MassTransit、Rebusライブラリを調べました。 その結果、Rebusに決定しました-このライブラリは習得が容易でありながら、サガの原理を完全に実現し、自由に使用できます。 NServiceBusとMassTransitは、多数の追加機能を備えたより洗練されたツールです。 これらはタスクの一部として必要ではありませんでしたが、より複雑なロジックを持つ将来のプロジェクトで使用することが適切な場合があります。



All Articles