メッセージ・キューとメッセージを使用してステータス・テーブルを適用して分散トランザクションを除去(転送)

5432 ワード

データ量が大きいため、ほとんどのWebアプリケーションでは、多くのデータベース・インスタンスを導入する必要があります.これにより、複数のデータベース・インスタンスのデータを変更する必要があるユーザー・アクションがある場合があります.従来の解決策は、分散トランザクションを使用してデータのグローバル一貫性を保証することであり、古典的な方法は、2段階コミットプロトコルを使用することです. 
長い間、分散トランザクションが提供してきた優雅なグローバルACIDは、アプリケーション開発者の心を麻酔することを保証し、多くの人が雷池を一歩越えることができず、分散トランザクションのない世界がどうなるか想像しています.今ではMySQLやPostgreSQLのようなローエンドユーザー向けのオープンソースデータベースで分散トランザクションがサポートされており、開発者は分散トランザクションがシステムにダメージを与えるかどうかを考えずに酔っ払っています. 
実際には、分散トランザクションが提供するACID保証は、システムの可用性、パフォーマンス、および伸縮性を損なうことを代価として提供されます.分散トランザクションに参加する各データベース・インスタンスが正常に動作することを前提に、分散トランザクションは正常に動作し、1つの動作が正常でない限り、トランザクション全体が完了しません.これにより、システムの可用性は、分散トランザクションに参加する各インスタンスの可用性の積に相当し、インスタンスが多ければ多いほど可用性の低下が顕著になります.パフォーマンスと伸縮性の観点から、まず、トランザクションの合計持続時間は通常、1つのトランザクションの各オペレーションが通常順次実行されるため、トランザクションの応答時間が大幅に増加します.次に、一般的なWebアプリケーションのトランザクションは大きくなく、単機操作時間も数ミリ秒から1ミリ秒未満であるが、分散トランザクションに関連すると、コミット時のノード間のネットワーク通信往復プロセスもミリ秒レベルであり、トランザクション応答時間への影響も無視できない.トランザクションの持続時間が長くなると、関連リソースに対するトランザクションのロック時間も増加し、システムのスループットと伸縮性に深刻な同時競合が増加する可能性があります. 
分散トランザクションには以上の問題があるため、eBayは設計上分散トランザクションではなく、他の方法でデータ整合性の問題を解決します.最も重要なテクノロジーは、メッセージキューとメッセージアプリケーションステータステーブルです. 
例を挙げる.システムに次の2つのテーブルがあると仮定します.
    
user(id, name, amt_sold, amt_bought)   
transaction(xid, seller_id, buyer_id, amount)   

ここでuserテーブルはユーザ取引要約情報を記録し、transactionテーブルは各取引の詳細を記録する. 
これにより、トランザクションを実行するときにトランザクションを使用する場合は、データベースに対して次の操作が必要になります.
begin;   
INSERT INTO transaction VALUES(xid, $seller_id, $buyer_id, $amount);   
UPDATE user SET amt_sold = amt_sold + $amount WHERE id = $seller_id;   
UPDATE user SET amt_bought = amt_bought + $amount WHERE id = $buyer_id;   
commit; 

  
すなわちtransactionテーブルに取引情報を記録し,売り手と買い手の状態を更新する. 
transactionテーブルとuserテーブルが異なるノードに格納されていると仮定すると、上記のトランザクションは分散トランザクションです.この分散トランザクションを削除するには、2つのサブトランザクションに分割します.1つのtransactionテーブルを更新し、1つのuserテーブルを更新することはできません.transactionテーブルの更新に成功すると、userの更新に失敗し、システムが一貫した状態に戻らない可能性があります. 
解決策は、メッセージキューを使用することです.次のように、トランザクションを開始し、transactionテーブルを更新した後、userテーブルを直接更新するのではなく、userテーブルに対する更新をメッセージキューに挿入します.もう1つの非同期タスクがキューの内容をポーリングして処理します. 
  • begin;   
    INSERT INTO transaction VALUES(xid, $seller_id, $buyer_id, $amount);   
    put_to_queue "update user("seller", $seller_id, amount);   
    put_to_queue "update user("buyer", $buyer_id, amount);   
    commit;   
    for each message in queue   
    begin;   
    dequeue message;   
    if message.type = "seller" then   
    UPDATE user SET amt_sold = amt_sold + message.amount WHERE id = message.user_id;   
    else   
    UPDATE user SET amt_bought = amt_bought + message.amount WHERE id = message.user_id;   
    end   
    commit;   
    end   

  • 上記のソリューションは完璧に見えますが、実際には分散型の問題は解決されていません.最初のトランザクションが分散操作に関係しないように、メッセージキューはtransactionテーブルと同じストレージリソースを使用する必要がありますが、2番目のトランザクションをローカルにするには、メッセージキューストレージはuserテーブルと一緒に使用する必要があります.この二つは同時に満足することはできない. 
    メッセージに操作べき乗などがある場合、つまり、1つのメッセージが複数回適用されるのと1回適用される効果が同じである場合、メッセージキューをtransactionテーブルに一緒に配置し、2番目のトランザクションでメッセージを適用してからメッセージキューから削除するだけで、上記の問題は解決されます.メッセージキューストレージはuserテーブルと一緒にないため、メッセージを適用した後、適用したメッセージをキューから削除する時間がない場合、システムが障害を起こす可能性があります.このとき,システムが復元されると,このメッセージが再適用され,べき乗等性のために複数回適用しても正しい結果が得られる. 
    しかし、実際には、上記のUPDATE操作のようなべき乗等性を有することは困難であり、1回の実行と複数回の実行の終了は明らかに異なる.この問題を解決する方法は、別のテーブルを使用して、正常に適用されたメッセージを記録し、このテーブルはuserテーブルと同じストレージを使用することである.次のテーブルmessage_を追加するとします.applied(msg_id)は、適用に成功したメッセージを記録し、最終的な解決策は以下の通りである.
  • begin;   
    INSERT INTO transaction VALUES(xid, $seller_id, $buyer_id, $amount);   
    put_to_queue "update user("seller", $seller_id, amount);   
    put_to_queue "update user("buyer", $buyer_id, amount);   
    commit;   
    for each message in queue   
    begin;   
    SELECT count(*) as cnt FROM message_applied WHERE msg_id = message.id;   
    if cnt =  then   
    if message.type = "seller" then   
    UPDATE user SET amt_sold = amt_sold + message.amount WHERE id = message.user_id;   
    else   
    UPDATE user SET amt_bought = amt_bought + message.amount WHERE id = message.user_id;   
    end   
    INSERT INTO message_applied VALUES(message.id);   
    end   
    commit;   
    if          
    dequeue message   
    DELETE FROM message_applied WHERE msg_id = message.id;   
    end   
    end   

  • 詳しく分析してみましょう.
    1、メッセージキューはtransactionと同じインスタンスを使用するため、最初のトランザクションは分散操作に関与しない. 
    2、message_appliedとuserテーブルは同じインスタンスでも一貫性を保証します. 
    3、2番目のトランザクションが終了すると、dequeue messageの前にシステムが障害を起こす可能性があります.障害が発生すると、メッセージキューからメッセージが再び取り出されますが、message_appliedテーブルは、このメッセージが適用されたことを検出し、このメッセージをスキップして正しい動作を実現することができる. 
    4、最後に正常に適用され、メッセージキューから削除されたメッセージがmessage_から削除されます.appliedテーブルから削除するとmessage_appliedテーブルは、小さな状態で保証されています(消去しなくてもよいし、システムの正確性に影響を与えません).メッセージキューとmessage_のためappliedは、異なるインスタンスでdequeue messageの後、message_に対応します.appliedレコードが削除される前に障害が発生する可能性があります.しかし、この時点で障害が発生しました.message_appliedテーブルにはごみの内容がいくつか残っていますが、システムの正確性には影響しません.また、これらのごみの内容も正しく整理できます. 
    分散トランザクションの一貫性の保証がないため、上記のスキームを使用すると、システムに障害が発生した場合、システムは短時間で不一致になります.しかし、メッセージキューとメッセージ適用ステータステーブルに基づいて、最終的にシステムを一貫性に復元することができます.メッセージ・キュー・スキームを使用すると、2つのデータベース・インスタンス間の緊密な結合が解除され、そのパフォーマンスと伸縮性は分散トランザクションとは比べものにならない. 
    もちろん、分散トランザクションの使用はアプリケーション開発の簡素化に役立ち、メッセージ・キューの使用には明らかに多くの作業量が必要であり、両者にはメリットとデメリットがあります.個人的な観点では、時間が迫っているシステムやパフォーマンスに対する要求が低いシステムでは、分散トランザクションを採用して開発効率を速め、時間の需要がそれほど厳しくなく、パフォーマンスに対する要求が高いシステムでは、メッセージキュースキームの使用を考慮する必要があります.分散トランザクションを使用していたシステムが安定し、パフォーマンスが要求されていたシステムでは、メッセージ・キュー・スキーマを使用して再構築してパフォーマンスを最適化できます.