web-dev-qa-db-ja.com

データ同期のためのシンプルなアプローチ

モバイルデバイスのローカルデータベースをサーバーのデータベースと同期させます。この同期は、たとえばユーザーがオンラインになったときに起こり、ローカルデータベースとリモートデータベースを可能な変更で更新します。

2つの方法について考え、そのうちの1つをすでに実装しましたが、それはかなり複雑であり、そのパフォーマンスとエラーが発生しやすいことについて少し心配しています。

アプローチ1

私が実装したアプローチは、すべてのサーバーでの同期を実行します。各アイテムの最後のサーバー更新タイムスタンプと、削除対象としてマークされたアイテムのフラグをクライアントのデータベースに保存します。私はすべてのアイテムを(タイムスタンプとフラグを付けて)サーバーに送信し、サーバーは、クライアントの最終更新タイムスタンプがサーバーに保存されている最終更新タイムスタンプと等しい場合にのみ、挿入、更新する必要があるアイテムを決定します(クライアントがアイテムの最新バージョンを持っていることを意味します)および何を削除する必要があります。次に、データベースに新しいクエリを実行し、すべてをクライアントに送信します。クライアントはそのローカルデータベースを結果で上書きします。詳細に:

  1. クライアントはすべてのアイテムをサーバーに送信します(これは、作成/更新/削除が必要なアイテムをフィルタリングすることで調整できますが、今のところすべてを送信します)。アイテムには、サーバーの最終更新タイムスタンプと、削除するようにマークされている場合はフラグが付けられます。

  2. サーバーはユーザーのすべてのアイテムをクエリします

  3. アイテムを更新/削除できるかどうかを判断するために、すべてのアイテムのタイムスタンプをチェックするプログラムアルゴリズム。クライアントのアイテムの最後のサーバー更新タイムスタンプがサーバーに保存されているタイムスタンプと異なる場合(つまり、誰かがその間にアイテムを更新した場合)、更新/削除は拒否されます。ここに多くのリストを作成します。挿入する必要があるアイテムの1つ(アイテムはサーバーのクエリ結果に含まれません)、更新するアイテム、削除するアイテム、タイムスタンプが原因で更新できないアイテムタイムスタンプが異なるため削除できないアイテムです。

  4. 挿入/更新/削除のトランザクション。3で作成したリストを使用します。

  5. クエリを実行して、ユーザーのすべてのアイテムを再度取得します。

  6. このクエリの結果を、更新または削除できなかったアイテムのリストと一緒にクライアントに送信します。これにより、クライアントは何が失敗したのかを知ることができます。

このアプローチの問題は、複数のクエリとプログラムによる処理です。別のクライアント(一部のアイテムは複数のユーザーを持つことができます)がこれらのクエリの間に何かを更新し、更新が失われる可能性があります。同期の問題を回避するために、クエリを圧縮し(単一のトランザクションでなんとかしてすべてを実行し、ストアドプロシージャを使用する可能性がある)、行をロックする必要があると思います。 SQLに関する私の知識はselect...wherejoinですので、これを行うためのより良い方法がないかもしれません。

アプローチ2

これらの複雑さのため、おそらくもっと簡単な方法があるかと考えていました。私が持っていたもう1つのアイデアは、クライアントでの同期を実行することでした。したがって、最初にすべてのデータをクエリして更新を行います(この場合も、最終更新のタイムスタンプと削除フラグが必要です-今回は、同期ロジックがクライアントにあり、ここで複数のユーザーを心配する必要はありません) )、同期結果をサーバーに送信します。サーバーは上書きを実行します。詳細に:

  1. ユーザー向けのアイテムをダウンロード

  2. クライアントでアルゴリズムを同期します。これは、3。のサーバーで行う方法と似ています(アプローチ1)。

  3. アイテムをサーバーにアップロードすると、サーバーはユーザーのアイテムをこれで上書きします。

もちろん、ここでの問題は、このクライアントが同期している間に別のクライアントがサーバー内のアイテムを更新できることであり、結果をアップロードすると、以前のクライアントの更新は失われます。ハッシュでこれを回避できるかもしれないと思ったので、ダウンロードされたデータにハッシュがあり、データベースが同期結果で上書きされる場合、最初にクエリを実行して、同じハッシュであることを確認します(エラーが返されない場合)。しかし、再び私はサーバーで複数のクエリを使用しているので、これが推奨される方法かどうかはわかりません。

この件についてのご意見をいただければ幸いです。私の現在のアプローチは最高ですか?または、クライアント同期を使用する必要があります(これにより、サーバーのワークロードも削減されます。これは良いことです)が、上書きの可能性をどのように回避するのでしょうか。ハッシュは良い考えですか。私のアプリには非常に厳密な要件はありません。これは、いくつかの追加機能を備えた共有ToDoリストのようなものであり、一般的なもの(ファイル共有サービスなど)や健康、科学などのためのものではないので、更新は失われます、それは世界の終わりではありません。 100%の正確さよりも、パフォーマンスと実装の容易さを重視しています。もちろん、エラーが発生しやすくなればなるほど、効果は高くなります。

追伸これに関する推奨事項(コメントとして)を読んでいただければ幸いです。同期戦略に関する優れたリソースです。私は一部でScala、Play 2.4、Akkaを使用しているため、この方向の推奨も非常に役立ちます。

7
Ixxzz
  • 複数のクライアント間の同期は非常に複雑な作業です。
  • 同期ではなく、bashトランザクション処理と呼びましょう。
  • "last server update timestamp"を取り除きましょう。すべてのトランザクションが、サーバーによって生成されたタイムスタンプを持つプリプロセステーブルに挿入されます。
  • 非同期にしましょう。データの処理速度が十分であれば、ユーザーは気付かないはずです。
  • クライアントはジョブID(ユーザーIDとタイムスタンプとその他のいくつかのrandonデータのハッシュ)を生成し、それをサーバーにプッシュします。
  • サーバーはジョブテーブルにジョブIDを保存し、タイムスタンプを割り当てます
  • クライアントはそのジョブに含まれるトランザクションを送信します enter image description here

    ITEM IDは、ジョブIDが生成されたのと同じ方法で、ハッシュ関数を使用してクライアントが生成します。

  • 数秒ごとに実行されるプロセスは、最新の保留中のジョブを取得し、すべてのトランザクションの処理に進みます。

  • ITEMが以前に消去されたために実行できなかったトランザクションは、拒否テーブルに挿入されます
  • トランザクションが拒否を含むクライアントにプッシュされた後の現在のデータ(クライアントデータは削除され、新しいデータが挿入され、クライアントはそれを更新として認識します)
  • ジョブはトランザクションなしにすることができます。つまり、ジョブは更新リクエストのみです。
  • 上書きは避けられませんが、古いバージョンのアイテムを上書きする代わりに、競合が発生したときにエントリを複製することを選択できます。
5

ユースケースに問題がないかどうかに応じて、別のアプローチが考えられます。異なるクライアントの異なるバージョン間で同期しようとする代わりに、更新中はそれらをロックアウトします。

たとえば、次のようなことができます。

update locktable set current_user='user61852' where current_user=''

もちろん、locktableは単一行(単一列)のテーブルです。失敗した場合(current_userはまだ自分のIDではありません)、少しスリープしてから再試行してください。

成功したら、必要に応じてデータを(再)クエリし、挿入などを行います。次に絶対に確実にしてください。すべての状況で、current_userが''に戻されます(したがって、おそらくタイムアウトも処理します)。

もちろん、特に朝9時に誰もがやりたいことがあり、転送することがたくさんある場合には、いくつかの欠点があります。

2
Glenn Rogers