同步客户端-服务器数据库


82

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

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

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

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

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

Answers:


93

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

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

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

因此,假设唯一更改的记录是#125。您要么确定两个版本之一自动“获胜”,然后覆盖另一个版本,要么需要支持协调阶段,用户可以确定哪个版本(服务器或客户端)是正确的版本,而覆盖另一个版本。

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

[假设#125可以由第二个客户端(客户端B)修改,则尚未同步的客户端B可能会提供同一记录的另一个版本,从而使先前的冲突解决方法无济于事]

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

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

概括起来,主要问题是:

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

参考书目:

  • 当然,有关更多信息,请参见Wikipedia

  • Vdirsyncer作者的简单同步算法

  • OBJC关于数据同步的文章

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

  • 无冲突的复制数据类型

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

  • 亚历山大·特劳(Alexander Traud),尤尔根·纳格勒·伊莱因(Juergen Nagler-Ihlein),弗兰克·卡格(Frank Kargl)和迈克尔·韦伯(Michael Weber)。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)和TS,迈鲍姆(Maibaum),1981年。抽象数据类型+同步-面向消息的编程方法-。第五届软件工程国际会议论文集(1981年3月9日至12日,美国加利福尼亚州圣地亚哥)。国际软件工程大会。IEEE Press,Piscataway,NJ,263-272。

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

Dr.Dobbs网站:

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

来自arxiv.org:

  • 无冲突的复制JSON数据类型-本文描述了JSON CRDT的实现(无冲突的复制数据类型-CRDT-是支持并发修改并保证此类并发更新的收敛性的数据结构家族)。

谢谢您的回答。我对阅读有关您所概述问题的常用/可能的解决方案(优点,缺点,比较)非常感兴趣。
斯科特·桑德斯

我想您已经检查了Wikipedia及其链接的内容,对吗?
p.marino

3
+1这是一篇很棒的文章,其中包含有关该问题的非常重要的信息。遗漏的一点:同步已删除的记录。
Stefan Steinegger,2010年

7
我倾向于将“已删除”视为“已更新”的特例,尤其是因为在这种情况下,我倾向于“逻辑删除”而不是“物理删除”。因此,对我而言,在主端或从属端“删除”意味着“特殊的boolean is-deleted标志已被翻转”比什么都重要。
p.marino 2010年

谢谢。我在另一篇文章(dr.dobbs)中添加了一个链接,如果可以找到其他内容,将更新参考书目。
p.marino

9

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

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

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


3
时钟必须同步
tofutim

6

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

我正在研究一种解决方案,其中必须将对一个客户端所做的更改传播到其他客户端。而且我只是实施了概念验证实施(服务器和客户端),它的工作原理很像。

基本上,每个客户端都将增量更改发送到服务器。例如,资源ID ABCD1234已从值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/zh-CN/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/zh-CN/documentation/articles/app-service-mobile-xamarin-forms-get-started-offline-data/


0

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


0

让我们称其为CUDR Sync问题(我不喜欢CRUD-因为Create / Update / Delete是写操作,应该配对在一起)

也可以从优先写在线的角度看问题。脱机写入方法存在唯一标识符冲突的问题,并且同一交易的多个网络调用增加了风险(或成本)。

我个人发现在线写优先方法更易于管理(因此它将是唯一的事实来源-从那里同步所有其他信息)。在线写入方法将不要求用户先离线写入-他们将通过获得良好的在线写入响应表来离线写入。

他可能会先离线阅读,并在网络可用时尽快从网上获取数据并更新本地数据库,然后更新ui...。

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


此外,离线写入方法会在应用卸载时出现问题,所有未在线上传的数据都将被删除
DragonFire
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.