クライアント/サーバーデータベースの同期


85

中央サーバー上のデータを、常にオンラインであるとは限らないクライアントアプリケーションと同期するためのいくつかの一般的な戦略を探しています。

私の特定のケースでは、sqliteデータベースを備えたAndroid電話アプリケーションとMySQLデータベースを備えたPHPWebアプリケーションがあります。

ユーザーは、電話アプリケーションとWebアプリケーションで情報を追加および編集できるようになります。電話がサーバーとすぐに通信できない場合でも、1か所で行った変更がすべての場所に反映されるようにする必要があります。

電話からサーバーに、またはその逆にデータを転送する方法には関心がありません。たとえば、MySQLで利用可能なレプリケーション機能を使用できないという理由だけで、特定のテクノロジについて言及しています。

クライアント/サーバーデータの同期の問題が長い間発生していることを知っています。問題を処理するためのパターンに関する情報(記事、書籍、アドバイスなど)が必要です。同期を処理して長所、短所、およびトレードオフを比較するための一般的な戦略について知りたいのですが。

回答:


96

最初に決定する必要があるのは、変更が競合する場合にどちらの側が「信頼できる」と見なされるかについての一般的なポリシーです。

つまり、レコード#125が1月5日の午後10時にサーバーで変更され、同じレコードが1月5日の午後11時にいずれかの電話(クライアントAと呼びます)で変更されたとします。最後の同期は1月3日でした。次に、ユーザーは、たとえば1月8日に再接続します。

クライアントとサーバーの両方が最後の同期の日付を知っているという意味で、何を変更する必要があるかを特定するのは「簡単」です。したがって、最後の同期以降に作成または更新されたもの(詳細については以下を参照)を調整する必要があります。

したがって、変更されたレコードは#125だけであるとします。2つのうちの一方が自動的に「勝ち」、もう一方を上書きすることを決定するか、ユーザーがどちらのバージョン(サーバーまたはクライアント)が正しいかを判断して他方を上書きできる調整フェーズをサポートする必要があります。

この決定は非常に重要であり、クライアントの「役割」に重みを付ける必要があります。特に、クライアントとサーバーの間に潜在的な競合がある場合だけでなく、異なるクライアントが同じレコードを変更する可能性がある場合。

[#125が2番目のクライアント(クライアントB)によって変更される可能性があると仮定すると、まだ同期されていないクライアントBが同じレコードのさらに別のバージョンを提供し、以前の競合解決が無効になる可能性があります]

上記の「作成または更新された」ポイントに関して...レコードがクライアントの1つで作成された場合、どのようにしてレコードを適切に識別できますか(これが問題のドメインで理にかなっていると仮定します)?アプリがビジネスの連絡先のリストを管理しているとしましょう。クライアントAが新しく作成されたJohnSmithを追加する必要があると言い、サーバーに昨日クライアントDによって作成されたJohn Smithがある場合...別の人物ではないことを確認できないため、2つのレコードを作成しますか?この競合も調整するようにユーザーに依頼しますか?

クライアントはデータのサブセットの「所有権」を持っていますか?つまり、クライアントBがエリア#5のデータの「権限」になるように設定されている場合、クライアントAはエリア#5のレコードを変更/作成できますか?(これにより、競合の解決が容易になりますが、状況によっては実行不可能になる場合があります)。

要約すると、主な問題は次のとおりです。

  • デタッチされたクライアントが新しいレコードを作成する前にサーバーにアクセスしていない可能性があることを考慮して、「ID」を定義する方法。
  • 以前の状況では、ソリューションがどれほど洗練されていても、データの重複が発生する可能性があるため、これらを定期的に解決する方法と、「レコード#675」と見なされたものが実際にマージ/置き換えられたことをクライアントに通知する方法を予測する必要があります。レコード#543
  • 競合がフラットによって解決されるか(たとえば、「最後の同期以降に前者が更新されている場合、サーバーバージョンは常にクライアントよりも優先される」)、または手動介入によって解決されるかどうかを決定します
  • フィアットの場合、特にクライアントが優先されると判断した場合は、さらに変更が加えられる可能性のある、まだ同期されていない他のクライアントの処理方法にも注意する必要があります。
  • 前の項目では、データの粒度が考慮されていません(説明を簡単にするため)。私の例のように「記録」レベルで推論する代わりに、フィールドレベルで変更を記録する方が適切な場合があると言えば十分です。または、一度に一連のレコード(たとえば、個人レコード+住所レコード+連絡先レコード)を処理して、それらの集計を一種の「メタレコード」として扱います。

参考文献:

  • もちろん、これについてはウィキペディアで詳しく説明しています。

  • Vdirsyncerの作者による単純な同期アルゴリズム

  • データ同期に関するOBJCの記事

  • SyncML®:モバイルデータの同期と管理(O'Reilly Safariの本)

  • 競合のない複製データ型

  • Optimistic Replication YASUSHI SAITO(HP Laboratories)およびMARC SHAPIRO(Microsoft Research Ltd。)- ACM Computing Surveys、Vol。V、No。N、32005。

  • アレクサンダー・トラウド、ユルゲン・ナグラー・イライン、フランク・カルグル、マイケル・ウェーバー。2008.SyncMLの再利用による循環データ同期。モバイルデータ管理に関する第9回国際会議(MDM '08)の議事録。IEEE Computer Society、ワシントンDC、米国、165-172。DOI = 10.1109 / MDM.2008.10 http://dx.doi.org/10.1109/MDM.2008.10

  • Lam、F.、Lam、N。、およびWong、R.2002。モバイルXMLデータの効率的な同期。情報と知識管理に関する第11回国際会議の議事録(マクリーン、バージニア、米国、2002年11月4日から9日)。CIKM'02。ACM、ニューヨーク、ニューヨーク、153-160。DOI = http://doi.acm.org/10.1145/584792.584820

  • Cunha、PRおよびMaibaum、TS1981。リソース&equil; 抽象データ型+同期-メッセージ指向プログラミングの方法論-。ソフトウェアエンジニアリングに関する第5回国際会議の議事録(1981年3月9日から12日まで、米国カリフォルニア州サンディエゴ)。ソフトウェア工学に関する国際会議。IEEE Press、ニュージャージー州ピスカタウェイ、263-272。

(最後の3つはACMデジタルライブラリからのものであり、あなたがメンバーであるかどうか、または他のチャネルを通じてそれらを取得できるかどうかはわかりません)。

Dr.Dobbsサイトから:

  • 2004年5月19日、BillWagnerによるSQLServerCEおよびSQLRDAを使用したアプリの作成(デスクトップPCとモバイルPCの両方のアプリケーションを設計するためのベストプラクティス-Windows / .NET)

arxiv.orgから:

  • 競合のない複製されたJSONデータ型-このペーパーでは、JSON CRDTの実装について説明します(競合のない複製されたデータ型-CRDT-は、同時変更をサポートし、そのような同時更新の収束を保証するデータ構造のファミリーです)。

ご回答ありがとうございます。私はあなたが概説する問題に対して一般的に使用される/可能な解決策(賛否両論、比較)について読むことに非常に興味があります。
スコットサンダース

あなたはすでにウィキペディアとそれらがリンクしているものをチェックしたと思いますよね?
p.marino 2010

3
+1これは、その問題に関する非常に重要な情報が記載されたすばらしい投稿です。1つの欠けている点:削除されたレコードの同期。
Stefan Steinegger 2010

7
私は「削除済み」を「更新済み」の特殊なケースと見なす傾向があります。特に、このような状況では、「物理的削除」ではなく「論理的削除」を好む傾向があるためです。したがって、私にとってマスター側またはスレーブ側の「削除済み」とは、何よりも「特別なブール値が削除されたフラグが反転された」ことを意味します。
p.marino 2010

ありがとう。別の記事(dr.dobbs)へのリンクをもう1つ追加しました。他に何か見つけたら、参考文献を更新します。
p.marino 2010

9

すべてのテーブルにタイムスタンプ列を設定し、挿入または更新するたびに、影響を受ける各行のタイムスタンプ値を更新することをお勧めします。次に、すべてのテーブルを繰り返し処理して、タイムスタンプが宛先データベースにあるタイムスタンプよりも新しいかどうかを確認します。それが新しい場合は、挿入または更新する必要があるかどうかを確認してください。

観察1:行はソースデータベースから削除され、サーバーデータベースでも同じことを行う必要があるため、物理的な削除に注意してください。これを解決するには、物理​​的な削除を回避したり、すべての削除をタイムスタンプ付きのテーブルに記録したりします。このようなもの:DeletedRows = (id, table_name, pk_column, pk_column_value, timestamp)したがって、DeletedRowsテーブルのすべての新しい行を読み取り、table_name、pk_column、およびpk_column_valueを使用してサーバーで削除を実行する必要があります。

観察2:別のテーブルに関連するテーブルへのデータの挿入が失敗する可能性があるため、FKに注意してください。データ同期の前に、すべてのFKを非アクティブ化する必要があります。


3
時計は同期している必要があります
tofutim 2016

6

誰かが同様の設計の問題に取り組んでいて、複数のAndroidデバイス間で変更を同期する必要がある場合は、Android向けGoogleクラウドメッセージング(GCM)確認することをお勧めします

私は、あるクライアントで行われた変更を他のクライアントに伝播する必要がある1つのソリューションに取り組んでいます。そして、概念実証の実装(サーバーとクライアント)を実装したところ、それは魅力のように機能します。

基本的に、各クライアントはデルタ変更をサーバーに送信します。たとえば、リソースIDABCD1234が値100から99に変更されました。

サーバーは、これらのデルタ変更をデータベースに対して検証し、変更を承認して(クライアントが同期している)、データベースを更新するか、変更を拒否します(クライアントが同期していない)。

変更がサーバーによって承認されると、サーバーはGCMを介して他のクライアント(デルタ変更を送信したクライアントを除く)に通知し、同じデルタ変更を伝送するマルチキャストメッセージを送信します。クライアントはこのメッセージを処理し、データベースを更新します。

すばらしいのは、これらの変更がほぼ瞬時に伝播されることです!!! それらのデバイスがオンラインの場合。また、これらのクライアントにポーリングメカニズムを実装する必要はありません。

デバイスのオフライン時間が長すぎて、GCMキューで配信待ちのメッセージが100を超える場合、GCMはそれらのメッセージを破棄し、デバイスがオンラインに戻ったときに特別なメッセージを送信することに注意してください。その場合、クライアントはサーバーと完全に同期する必要があります。

CGMクライアントの実装を開始するには、このチュートリアルも確認してください。


5

これは、Xamarinフレームワークを使用している開発者に回答します/programming/40156342/sync-online-offline-dataを参照)

xamarinフレームワークでこれを実現する非常に簡単な方法は、Azureのオフラインデータ同期を使用することです。これにより、サーバーからオンデマンドでデータをプッシュおよびプルできます。読み取り操作はローカルで実行され、書き込み操作はオンデマンドでプッシュされます。ネットワーク接続が切断されると、接続が復元されるまで書き込み操作がキューに入れられてから実行されます。

実装はかなり単純です:

1)Azureポータルでモバイルアプリを作成します(https://tryappservice.azure.com/から無料で試すことができます

2)クライアントをモバイルアプリに接続します。 https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-xamarin-forms-get-started/

3)ローカルリポジトリを設定するためのコード:

const string path = "localrepository.db";

//Create our azure mobile app client
this.MobileService = new MobileServiceClient("the api address as setup on Mobile app services in azure");

//setup our local sqlite store and initialize a table
var repository = new MobileServiceSQLiteStore(path);

// initialize a Foo table
store.DefineTable<Foo>();

// init repository synchronisation
await this.MobileService.SyncContext.InitializeAsync(repository);
var fooTable = this.MobileService.GetSyncTable<Foo>();

4)次に、データをプッシュおよびプルして、最新の変更があることを確認します。

await this.MobileService.SyncContext.PushAsync();
await this.saleItemsTable.PullAsync("allFoos", fooTable.CreateQuery());

https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-xamarin-forms-get-started-offline-data/


0

Symmetricdsもご覧になることをお勧めします。これは、Androidシステムで使用できるSQLiteレプリケーションライブラリです。これを使用して、クライアントとサーバーデータベースを同期できます。また、クライアントごとにサーバー上に個別のデータベースを用意することをお勧めします。すべてのユーザーのデータを1つのmysqlデータベースに保持しようとすることが常に最良のアイデアであるとは限りません。特に、ユーザーデータが急速に増加する場合。


0

これをCUDR同期の問題と呼びましょう(CRUDは好きではありません-作成/更新/削除は書き込みであり、ペアにする必要があるため)

この問題は、write-offliine-firstまたはwrite-online-firstの観点からも見ることができます。write-offline-approachには、一意の識別子の競合に問題があり、同じトランザクションに対する複数のネットワーク呼び出しによってリスク(またはコスト)が増加します...

私は個人的に、write-online-firstアプローチの方が管理しやすいと感じています(したがって、他のすべてが同期される場所から、信頼できる唯一の情報源になります)。write-online-approachでは、ユーザーが最初にオフラインで書き込むことを許可しない必要があります。ユーザーは、オンライン書き込みからok応答フォームを取得することでオフラインで書き込みます。

彼は最初にオフラインで読み取り、ネットワークが利用可能になり次第、オンラインからデータを取得し、ローカルデータベースを更新してから、UIを更新する場合があります。

一意の識別子の競合を回避する1つの方法は、一意のユーザーID +テーブル名またはテーブルID +行ID(sqliteによって生成される)の組み合わせを使用することです...次に、同期されたブールフラグ列を使用します。他のすべてのIDが生成される一意のIDを取得するには、最初にオンラインで登録を行う必要があります...ここで問題は、クロックが同期されていない場合にも発生します-上記の誰かが...


また、書き込みオフラインのアプローチは、オンラインでアップロードされていないすべてのデータが削除され、アプリのアンインストールに問題があります
DragonFire
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.