私はこのようなシステムを約8年前にアプリ用に構築しましたが、アプリの使用が増えるにつれて進化する方法をいくつか共有できます。
まず、任意のデバイスからのすべての変更(挿入、更新、または削除)を「履歴」テーブルに記録しました。たとえば、誰かが「連絡先」テーブルの電話番号を変更した場合、システムはcontact.phoneフィールドを編集し、action = update、field = phone、record = [contact ID]、 value = [新しい電話番号]。その後、デバイスが同期するたびに、前回の同期以降の履歴アイテムがダウンロードされ、ローカルデータベースに適用されます。これは、上記の「トランザクションレプリケーション」パターンのように聞こえます。
1つの問題は、異なるデバイスでアイテムを作成できる場合にIDを一意に保つことです。これを始めたときはUUIDについて知らなかったので、自動インクリメントIDを使用し、中央サーバーで実行される複雑なコードを記述して、デバイスからアップロードされた新しいIDを確認し、競合がある場合は一意のIDに変更しました。ローカルデータベースのIDを変更するようにソースデバイスに指示します。新しいレコードのIDを変更するだけでそれほど問題はありませんでしたが、たとえば、連絡先テーブルに新しいアイテムを作成し、イベントテーブルに新しい関連アイテムを作成すると、外部キーも必要になります。チェックして更新します。
最終的に私はUUIDでこれを回避できることを学びましたが、それまでにデータベースがかなり大きくなり、完全なUUID実装がパフォーマンスの問題を引き起こすと心配しました。そのため、完全なUUIDを使用する代わりに、ランダムに生成された8文字の英数字キーをIDとして使用し始め、既存のコードを残して競合を処理しました。私の現在の8文字のキーとUUIDの36文字の間のどこかに、不必要な膨張なしに競合を解消するスイートスポットがあるはずですが、私はすでに競合解決コードを持っているので、それを試すことは優先事項ではありませんでした。
次の問題は、履歴テーブルが残りのデータベース全体の約10倍であったことです。これにより、ストレージが高価になり、履歴テーブルのメンテナンスが困難になる可能性があります。そのテーブル全体を維持することで、ユーザーは以前の変更をロールバックできますが、それはやり過ぎのように感じ始めました。そこで、デバイスが最後にダウンロードした履歴アイテムが履歴テーブルに存在しなくなった場合、サーバーは最近の履歴アイテムを提供せず、代わりにすべてのデータを含むファイルを提供するルーチンを同期プロセスに追加しましたそのアカウント。次に、cronジョブを追加して、90日以上経過した履歴アイテムを削除しました。つまり、ユーザーは90日未満の変更を引き続きロールバックでき、90日ごとに少なくとも1回同期すると、更新は以前と同様に増分されます。でも90日以上待つと
この変更により、履歴テーブルのサイズがほぼ90%減少したため、履歴テーブルを維持しても、データベースのサイズは10倍ではなく2倍になりました。このシステムのもう1つの利点は、必要に応じて履歴テーブルがなくても同期が機能することです(一時的にオフラインにするメンテナンスを行う必要がある場合など)。または、異なる価格でアカウントに異なるロールバック期間を提供することもできます。また、ダウンロードする変更が90日以上ある場合は、通常、完全なファイルの方が増分形式よりも効率的です。
今日からやり直した場合は、IDの競合チェックをスキップし、念のため、何らかのエラーチェックを行って、競合を排除するのに十分なキーの長さを目指します。しかし、履歴テーブルと、最近の更新の増分ダウンロードの組み合わせ、または必要な場合の完全ダウンロードはうまく機能しています。