6

私の PHP アプリケーション (symfony フレームワークと Propel ORM を使用して構築) では、MYSQL データベースにレコードを追加するときに、外部ベンダーが提供する Web サービス API を使用して外部 MYSQL データベースを更新する必要があります。

問題は、データベースの整合性を維持するためのベスト プラクティスは何かということです。たとえば、最初の更新が成功し、Web サービスが利用できないために 2 回目の更新が失敗した場合、次のいずれかを行う必要があります。

  1. 最初の更新のトランザクションをロールバックする、または
  2. Web サービスへの呼び出しをキャッシュし、サービスが利用可能になるまで Web サービスへの呼び出しを続ける
  3. 複数のデータベースの整合性を維持できるその他の手法。

具体的には、次のような構文を探しています

void RootMethod()
{
     using(TransactionScope scope = new TransactionScope())
     {
        try
         { 
          SomeMethod();
          scope.Complete();
          CallWebService();
         }
         catch
         {
             scope.abort();
          }
     }
}

しかし、かどうかはわかりません

  1. これはいいテクニックです
  2. または、これは C# のように symfony で実行可能ですか?

どう思いますか?

編集: 2 部構成の更新が必要な理由を尋ねる人もいました。これは、既存のバックエンド アプリケーションに接続するフロントエンド アプリケーションを作成しているためです。また、バックエンド アプリケーションを変更したくありません。したがって、必然的にいくつかの重複が発生します。したがって、データを同期する必要があります

別の編集: 2 つの部分のトランザクションは一緒に実行する必要があります。テーブルを同期するために cron ジョブを実行することは望ましくありません

4

8 に答える 8

5

大きな問題は、Webサービスへの重複した更新が重要かどうか、そしてそれらが検出できるかどうかです。重複を検出できる場合(通常は一意のトランザクション番号を使用)、または重複が問題にならない場合は、信頼性の高い2フェーズコミットスタイルのアプローチを構築できます。

Webサービスへの重複トランザクションを検出できず、更新がべき等でない場合は、運が悪いことになります。

これが基本的なアルゴリズムです。

begin transaction;
do local work;
save information for external call;
set an appropriate time for next attempt;
mark external call as not performed;
commit work;

begin transaction;
make external call;
if successful
   mark external call as performed (or delete the record)
else
   set the time for the next attempt
commit;

次に、通常のタスク、スレッド、または次のようなことを行うものが必要です。

for each record where the time for the next attempt <= now
    begin work;
    if the remote service has not performed this transaction
        make the remote call;
        if successful
            mark as done;
        else if too many attempts
            mark the transaction as permanently failed;
            alert operator;
        else
            set the time for the next attempt;
        endif
    else
        mark as done;
    endif

    commit;
 endfor

このアプローチは、すべての障害状態を確実に処理し、両方の作業が最終的に確実に行われるようにします。

基本的な失敗:

  1. 最初のコミットが完了する前の失敗:すべてがロールバックします。

  2. 最初のコミット後、Webサービスが完了する前の障害(これには、Webサービス自体の一時的な障害が含まれます):リモートWebサービストランザクションは、回復タスクによって再生されます。

  3. Webサービスが完了した後、2番目のコミットが完了する前の障害:重複したWebサービス・トランザクションがリカバリー・タスクによって検出され、ローカル・レコードがデキューされます。

  4. リカバリタスクの失敗:基本的に、2番目のトランザクションの失敗と同じです。

その他の注意事項:

  • 段階的なバックオフアプローチは、障害に対して役立ちます。サービスで一時的な障害が発生した場合は、再試行を遅くします。

  • 外部サービスに注文要件がある場合は、追加の構造が必要になる場合があります。

  • リカバリタスクの実装方法によっては、Webサービスの呼び出しをそのタスクに任せて、メインのアプリケーションフローに2番目のトランザクションを含めないようにすることができます。

追加要件への対応:「2つの部分のトランザクションは一緒に実行する必要があります。テーブルを同期するためにcronジョブを実行することは望ましくありません」

私がこの要件を読んだのは、「2つのシステムが失敗することは決してない」ということです。

一方または両方のシステムに障害が発生した場合、断片を拾い上げて調整するための何かが必要です。本格的なTPモニターを使用してトランザクションの調整を行うことも、特定のケースを処理する私の例のような単純なモニターを作成することもできます。いずれにせよ、障害状態の後で物事を正しく解決できるように、何が起こっていたかを追跡する何かがあります。

物事が常に一緒に発生することが本当に必要な場合(およびトランザクションメッセージキューまたは2フェーズコミットアプローチが機能しない場合)、両方のシステムのデータを同じデータベース(別名「リソースマネージャー」)に保存することをお勧めします。 )および単一のリソースマネージャートランザクションを持ちます。

複数のトランザクション間で2つの別個のシステムを一貫させるという要件を満たし、障害後にその後の調整を必要としないこの問題の解決策を入手した場合は、それを作成して、VLDBジャーナル、ACM TODS、またはIEEETKDEに公開する必要があります。 。

于 2009-02-23T11:32:43.127 に答える
4

これはトリッキーになるでしょう。信頼できるソリューションには 2 フェーズ コミットが必要ですが、特定のニーズに合わせて実装するには膨大な作業が必要です。

たぶん、優れた解決策は実際には求められていません。難しいパフォーマンスの制約を受けていますか? 通常、トランザクションは短時間で終了する必要があります...しかし、Web サービス呼び出しの周りでトランザクションを開いたままにしておく必要がありますか? これにより、データベースの全体的なスループットが(少なくとも)低下します...しかし、それは完全に許容できる場合があります。

あなたが示したアプローチには、ハード システム障害 (電源障害、ハードウェア障害など) の処理に問題があります。これを回避するには、メイン データベースに追跡を追加し、障害を処理するバックグラウンド プロセス/スタートアップ プロセスを追加する必要があります。かなり面倒ですが、確かに可能です。

一部の失敗は、修正できなくなる可能性があります (最初の部分は成功し、2 番目の部分は失敗し、別のトランザクションが同じデータを更新したため、最初の部分は元に戻すことができません)。それはすべて、正確なビジネスルールに依存します。トランザクションの取り消しは、実際には更新ではなくレコードのオフセットとして行われるため、会計システムが最も簡単です。

幸運を。

于 2009-02-22T03:24:17.647 に答える
3

ロールバックが実際に状況を改善することはないと思います。Webサービスがダウンしている場合、さらに電話をかけることは問題を悪化させるだけであり、ロールバックが行われたかどうかなどを心配する必要があります。

これは、スケジュールされた完全同期で行います。エラーの許容誤差はどれくらいですか?データベースの同期が少しずれてもよろしいですか?いくらですか?発生した問題を修正するためにシンクロナイザーを毎晩実行するのは大したことでしょうか?これについて心配しなければならないWebサービスはどのくらいの頻度でダウンしていますか?

失敗したWebサービス呼び出しの更新キューは適切なアイデアですが、Webサービスがダウンしている場合は、1つか2つだけでなく、一度に大量のWebサービスが発生する可能性があるため、とにかく停止。

本当にあなたの答えはそれらの質問に依存します。10分間0.01%同期していない場合、プログラム全体がクラッシュすると誤解しないでください。エラーの許容マージンを把握します。

于 2009-02-24T18:10:25.707 に答える
2

データベースの同期を維持するのは難しい作業です。使用しているデータによっては、変更内容を含む別のテーブルを追加してから、cronまたは別のコードで別のスクリプトを実行して、Webサービスを更新して同期させようとすることができますか。データベースに保存されている変更。変更が成功すると、変更がリモートサーバーに送信されていないことを示すフラグが削除されます。

データが挿入された後のローカルデータベースでは、フラグを使用して、まだライブにしないことを指定できます。その後同期されるすべてのデータにより、そのフラグは完全にコミットされたものに変更されます。

これを行う具体的な理由は何ですか?アプリケーション自体で2つのデータベースの同期を維持する必要があるのはなぜですか。代わりに、1時間ごとに同期できますか?

これには、データの状態追跡と、データが両端に正常にコミットされているかどうかが必要になります。

私の個人的な選択は1番です。それが絶対に不可能でない限り、ローカルトランザクションをロールバックします。2番を使用します。

于 2009-02-23T11:01:01.317 に答える
2

2 フェーズのトランザクション管理ロジックを自分で構築しようとしないでください。あなたはそれを誤解するでしょう、私を信じてください。C# のように proggy 環境で使用できる場合は、それを使用します。そうでない場合は、自分で構築しないでください。

ほとんどの場合、複数のオンライン データベースにトランザクションを分散するシステムを設計する方が簡単ですが( 1) ネットワークが利用できなくなった場合の回復力 ( 2) 高負荷に直面した場合の一貫したレイテンシー動作。

そのため、トランザクションをローカル リソースに限定します。

make a change to reliable store (I believe this would be called a "resource" in X/Open transaction parlance)
enqueue a record of that change in a disk-backed log

その後、スケジュールに従って (毎時、毎日など)

while not done
    pop item from queue or log
    sync that change with the external, remote resource

負荷が高いときは、キューがいっぱいになりますが、ネットワークの負荷とトランザクションの待ち時間は比較的一定に保たれます。これは、家庭の暖房費の毎月の予算計画に少し似ています。負荷が比較的低いときは、キューが空になります。

于 2009-02-26T15:57:56.553 に答える
1

わかりませんでした。アプリはPHPですか、それともC#ですか。それがC#(WCF)であり、WebサービスがWCFである(またはWS-AtomicTransactionをサポートしている)場合、これは可能です。

于 2009-02-23T11:11:36.237 に答える
1

この複数の更新のロジックをアプリケーションに配置するのではなく、いつ何を更新し、更新が失敗した場合に何を行うかを認識している外部プロセスを使用することもできます。たとえば、Oracle BPEL はそのようなプロセスです。さまざまなサービスを調整するように構成できます。例については、http://alisonatoracle.blogspot.com/2006_01_01_archive.htmlを参照してください。

ただし、サイズによっては、アプリにとってやり過ぎかもしれません...

于 2009-02-26T20:12:37.753 に答える