85

我正在寻找一些通用策略,用于将中央服务器上的数据与并不总是在线的客户端应用程序同步。

在我的特殊情况下,我有一个带有 sqlite 数据库的 android 手机应用程序和一个带有 MySQL 数据库的 PHP Web 应用程序。

用户将能够在电话应用程序和 Web 应用程序上添加和编辑信息。即使手机无法立即与服务器通信,我也需要确保在一个地方所做的更改会在各处反映出来。

我不关心如何将数据从手机传输到服务器,反之亦然。我提到我的特定技术只是因为我不能使用例如 MySQL 可用的复制功能。

我知道客户端-服务器数据同步问题已经存在了很长时间,并且希望获得有关处理该问题的模式的信息——文章、书籍、建议等。我想了解处理同步以比较优势、劣势和权衡的一般策略。

4

6 回答 6

99

您必须决定的第一件事是关于在发生冲突更改时哪一方被视为“权威”的一般政策。

即:假设记录 #125 在 1 月 5 日晚上 10 点在服务器上更改,并且在 1 月 5 日晚上 11 点在其中一部手机(我们称之为客户端 A)上更改了相同的记录。上次同步是在 1 月 3 日。然后用户在 1 月 8 日重新连接。

确定需要更改的内容是“容易的”,因为客户端和服务器都知道上次同步的日期,因此需要协调自上次同步以来创建或更新的任何内容(有关此内容的更多信息,请参见下文)。

所以,假设唯一改变的记录是#125。您要么决定两者中的一个自动“获胜”并覆盖另一个,要么您需要支持一个协调阶段,在该阶段用户可以决定哪个版本(服务器或客户端)是正确的,并覆盖另一个。

这个决定非常重要,您必须权衡客户的“角色”。特别是如果不仅在客户端和服务器之间存在潜在冲突,而且如果不同的客户端可以更改相同的记录。

[假设 #125 可以由第二个客户端(客户端 B)修改,则尚未同步的客户端 B 可能会提供同一记录的另一个版本,从而使之前的冲突解决没有实际意义]

关于上面的“创建或更新”点......如果记录源自其中一个客户端(假设这在您的问题域中有意义),您如何正确识别记录?假设您的应用管理业务联系人列表。如果客户 A 说您必须添加一个新创建的 John Smith,而服务器有一个由客户 D 昨天创建的 John Smith……您是否创建了两条记录,因为您不能确定它们不是不同的人?你会要求用户也调和这个冲突吗?

客户是否拥有数据子集的“所有权”?即,如果客户 B 被设置为区域 #5 数据的“权威”,客户 A 是否可以修改/创建区域 #5 的记录?(这将使一些冲突解决更容易,但可能证明对您的情况不可行)。

总结起来主要问题有:

  • 考虑到分离的客户端在创建新记录之前可能尚未访问服务器,如何定义“身份”。
  • 以前的情况,无论解决方案多么复杂,都可能导致数据重复,因此您必须预见如何定期解决这些问题以及如何通知客户他们认为的“记录#675”实际上已被合并/取代记录#543
  • 决定是否将通过法令解决冲突(例如“如果服务器版本自上次同步后已更新,则服务器版本总是胜过客户端”)或手动干预
  • 法定货币的情况下,特别是如果您决定客户端优先,您还必须注意如何处理可能会有更多变化的其他尚未同步的客户端。
  • 前面的项目没有考虑数据的粒度(为了使事情更易于描述)。可以说,与其在我的示例中那样在“记录”级别进行推理,您可能会发现在现场级别记录更改更合适。或者一次处理一组记录(例如人员记录+地址记录+联系人记录),将它们的聚合视为一种“元记录”。

参考书目:

  • 当然,更多关于这方面的内容,请访问Wikipedia

  • Vdirsyncer作者的一个简单的同步算法

  • OBJC 关于数据同步的文章

  • SyncML®:同步和管理您的移动数据(O'Reilly Safari 上的书籍)

  • 无冲突的复制数据类型

  • 乐观复制YASUSHI SAITO(惠普实验室)和 MARC SHAPIRO(微软研究有限公司) - ACM 计算调查,卷。V, No. N, 3 2005。

  • 亚历山大·特劳德、于尔根·纳格勒-伊莱因、弗兰克·卡格尔和迈克尔·韦伯。2008. 通过重用 SyncML 实现循环数据同步。在第九届移动数据管理国际会议 (MDM '08) 的论文集中。IEEE 计算机协会,华盛顿特区,美国,​​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 数据的高效同步。在第十一届信息与知识管理国际会议论文集上(美国弗吉尼亚州麦克莱恩,2002 年 11 月 4 日至 9 日)。CIKM '02. ACM,纽约,纽约,153-160。DOI= http://doi.acm.org/10.1145/584792.584820

  • Cunha, PR 和 Maibaum, TS 1981。抽象数据类型 + 同步 - 面向消息编程的方法论 -。在第五届国际软件工程会议论文集(美国加利福尼亚州圣地亚哥,1981 年 3 月 9 日至 12 日)。国际软件工程会议。IEEE 出版社,新泽西州皮斯卡塔韦,263-272。

(最后三个来自 ACM 数字图书馆,不知道您是否是会员,或者是否可以通过其他渠道获得)。

Dr.Dobbs网站:

  • 使用 SQL Server CE 和 SQL RDA 创建应用程序 Bill Wagner 2004 年 5 月 19 日(为台式机和移动 PC 设计应用程序的最佳实践 - Windows/.NET)

来自 arxiv.org:

  • A Conflict-Free Replicated JSON Datatype - 该论文描述了 JSON CRDT 实现(无冲突复制数据类型 - CRDT - 是支持并发修改并保证此类并发更新收敛的数据结构系列)。
于 2010-08-06T14:45:49.117 回答
10

我建议您在每个表中都有一个时间戳列,并且每次插入或更新时,更新每个受影响行的时间戳值。然后,您遍历所有表,检查时间戳是否比目标数据库中的时间戳新。如果它较新,请检查是否必须插入或更新。

观察 1:注意物理删除,因为行是从源数据库中删除的,您必须在服务器数据库中执行相同操作。您可以解决此问题,避免物理删除或使用时间戳记录表中的每个删除。像这样:DeletedRows = (id, table_name, pk_column, pk_column_value, timestamp)因此,您必须读取 DeletedRows 表的所有新行,并使用 table_name、pk_column 和 pk_column_value 在服务器上执行删除。

观察 2:注意 FK,因为在与另一个表相关的表中插入数据可能会失败。您应该在数据同步之前停用每个 FK。

于 2012-04-24T18:30:23.813 回答
6

如果有人正在处理类似的设计问题并且需要在多个 Android 设备之间同步更改,我建议您查看Google Cloud Messaging for Android (GCM)。

我正在研究一种解决方案,其中在一个客户端上所做的更改必须传播到其他客户端。我刚刚实现了一个概念验证实现(服务器和客户端),它就像一个魅力。

基本上,每个客户端都会向服务器发送增量更改。例如,资源 ID ABCD1234 已从值 100 更改为 99。

服务器根据其数据库验证这些增量更改,并批准更改(客户端同步)并更新其数据库或拒绝更改(客户端不同步)。

如果更改被服务器批准,则服务器通过 GCM 通知其他客户端(不包括发送 delta 更改的客户端),并发送携带相同 delta 更改的多播消息。客户端处理此消息并更新其数据库。

很酷的是,这些变化几乎是瞬间传播的!!!如果这些设备在线。而且我不需要对这些客户端实施任何轮询机制。

请记住,如果设备离线时间过长并且有超过 100 条消息在 GCM 队列中等待传递,GCM 将丢弃这些消息并在设备重新在线时发送一条特殊消息。在这种情况下,客户端必须与服务器进行完全同步。

另请查看本教程以开始使用 CGM 客户端实现。

于 2015-01-16T12:12:09.343 回答
5

这回答了使用 Xamarin 框架的开发人员(请参阅https://stackoverflow.com/questions/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/

于 2016-10-21T04:19:48.690 回答
0

我建议你也看看Symmetricds。它是一个可用于 android 系统的 SQLite 复制库。您可以使用它来同步您的客户端和服务器数据库,我还建议在服务器上为每个客户端设置单独的数据库。尝试将所有用户的数据保存在一个 mysql 数据库中并不总是最好的主意。特别是如果用户数据将快速增长。

于 2018-06-06T11:56:38.203 回答
0

让我们称之为CUDR 同步问题(我不喜欢 CRUD - 因为 Create/Update/Delete 是写入,应该配对在一起)

这个问题也可以从离线写入优先在线写入优先的角度来看待。write-offline-approach 存在唯一标识符冲突的问题,并且同一事务的多个网络调用增加了风险(或成本)......

我个人发现在线写入优先的方法更易于管理(因此它将成为唯一的事实来源 - 其他所有内容都是同步的)。write-online-approach 将要求不要让用户首先离线写入 - 他们将通过在线写入获得 ok 响应表单来离线写入。

他可以先离线阅读,一旦网络可用,就从在线获取数据并更新本地数据库,然后更新 ui....

避免唯一标识符冲突的一种方法是使用唯一用户 id + 表名或表 id + 行 id 的组合(由 sqlite 生成)......然后使用同步的布尔标志列......但仍然是注册必须首先在线完成以获得将生成所有其他 id 的唯一 id ...这里的问题也将是如果时钟不同步 - 上面有人提到过...

于 2019-12-12T08:52:38.843 回答