我有一种感觉,一定存在客户端-服务器同步模式。但我完全没能在谷歌上找到一个。

情况非常简单 - 服务器是中心节点,多个客户端连接并操作相同的数据。数据可以被分割成原子,如果发生冲突,服务器上的任何内容都具有优先权(以避免让用户陷入冲突解决)。由于可能存在大量数据,因此首选部分同步。

对于这种情况是否有任何模式/良好实践,或者如果您不知道任何模式/良好实践 - 您的方法是什么?

以下是我现在想到的解决方法:与数据并行,将保存修改日志,并为所有事务添加时间戳。当客户端连接时,它会以合并的形式接收自上次检查以来的所有更改(服务器遍历列表并删除添加内容,然后删除,合并每个原子的更新等)。瞧,我们是最新的。

另一种方法是保留每条记录的修改日期,而不是执行数据删除,只需将它们标记为已删除。

有什么想法吗?

有帮助吗?

解决方案

您应该了解分布式变更管理的工作原理。查看 SVN、CVS 和其他管理增量工作的存储库。

您有几个用例。

  • 同步更改。您的更改日志(或增量历史记录)方法看起来很适合此目的。客户端将其增量发送到服务器;服务器合并增量并将其分发给客户端。这就是典型案例。数据库称之为“事务复制”。

  • 客户端失去同步。通过备份/恢复或由于错误。在这种情况下,客户端需要从服务器获取当前状态,而不需要遍历增量。这是从主控到细节的副本,增量和性能见鬼去吧。这是一次性的事情;客户端坏了;不要尝试优化它,只需实现可靠的副本即可。

  • 客户表示怀疑。在这种情况下,您需要将客户端与服务器进行比较,以确定客户端是否是最新的并且需要任何增量。

您应该遵循数据库(和 SVN)设计模式,对每个更改按顺序编号。这样,客户端可以在尝试同步之前提出一个简单的请求(“我应该进行什么修订?”)。即便如此,客户端和服务器处理查询(“自 2149 年以来的所有增量”)也非常简单。

其他提示

作为团队的一员,我做过很多涉及数据同步的项目,所以我应该有能力回答这个问题。

数据同步是一个相当广泛的概念,有太多值得讨论的内容。它涵盖了一系列不同的方法及其优点和缺点。以下是基于两个角度的可能分类之一:同步/异步、客户端/服务器/点对点。同步实施严重依赖于这些因素、数据模型复杂性、传输和存储的数据量以及其他要求。因此,在每种特定情况下,选择应该倾向于满足应用程序要求的最简单的实现。

基于对现有现成解决方案的回顾,我们可以描述同步的几个主要类别,同步对象的粒度不同:

  • 整个文档或数据库的同步用于基于云的应用程序,例如 Dropbox、Google Drive 或 Yandex.Disk。当用户编辑并保存文件时,新的文件版本将完全上传到云端,覆盖早期的副本。如果发生冲突,两个文件版本都会被保存,以便用户可以选择更相关的版本。
  • 键值对的同步可以在具有简单数据结构的应用程序中使用,其中变量被认为是原子的,即不分为逻辑组件。此选项类似于同步整个文档,因为值和文档都可以完全覆盖。然而,从用户的角度来看,文档是一个由很多部分组成的复杂对象,而键值对只是一个短字符串或一个数字。因此,在这种情况下,我们可以使用更简单的冲突解决策略,考虑更相关的值(如果它是最后更改的)。
  • 同步结构为树或图的数据用于更复杂的应用程序,其中数据量足够大,可以在每次更新时完整地发送数据库。在这种情况下,冲突必须在单个对象、领域或关系的层面上解决。我们主要关注这个选项。

因此,我们将我们的知识融入到这篇文章中,我认为这对于每个对此主题感兴趣的人都非常有用 => 基于核心数据的 iOS 应用程序中的数据同步(http://blog.denivip.ru/index.php/2014/04/data-syncing-in-core-data-based-ios-apps/?lang=en)

您真正需要的是操作转换(OT)。在许多情况下,这甚至可以解决冲突。

这仍然是一个活跃的研究领域,但有各种OT算法的实现。我已经参与了这样的研究多年了,所以请告诉我这条路线是否对您有兴趣,我很乐意为您提供相关资源。

问题不是很清楚,但如果我是的话,我会调查乐观锁定您。 它可以使用服务器为每条记录返回的序列号来实现。当客户端尝试保存记录时,它将包含从服务器接收的序列号。如果序列号与接收到更新时数据库中的序列号匹配,则允许更新并且序列号递增。如果序列号不匹配,则不允许更新。

我在大约8年前为一个应用程序构建了一个这样的系统,随着应用程序使用量的增长,我可以分享它已经发展的几种方式。

我首先将每个更改(插入,更新或删除)从任何设备记录到“历史记录”中。表。因此,例如,如果某人在“联系人”中更改了他们的电话号码。表,系统将编辑contact.phone字段,并添加具有action = update,field = phone,record = [contact ID],value = [new phone number]的历史记录。然后,只要设备同步,它就会下载自上次同步以来的历史记录项,并将它们应用到本地数据库。这听起来像是“交易复制”。上面描述的模式。

一个问题是在可以在不同设备上创建项目时保持ID唯一。我开始时我不知道UUID,因此我使用了自动递增ID并编写了一些在中央服务器上运行的复杂代码来检查从设备上传的新ID,如果存在冲突则将其更改为唯一ID,以及告诉源设备更改其本地数据库中的ID。只是更改新记录的ID并没有那么糟糕,但是如果我创建联系表中的新项目,然后在事件表中创建一个新的相关项目,现在我还需要外键。检查并更新。

最终我了解到UUID可以避免这种情况,但到那时我的数据库变得非常庞大,我担心完整的UUID实现会产生性能问题。因此,我开始使用随机生成的8个字符的字母数字键作为ID,而不是使用完整的UUID,我保留了现有的代码来处理冲突。在我当前的8个字符的密钥和UUID的36个字符之间,必须有一个可以消除冲突而没有不必要的膨胀的最佳位置,但是由于我已经有了冲突解决代码,所以它并不是优先考虑的问题。

下一个问题是历史表大约是整个数据库的10倍。这使得存储变得昂贵,并且历史表上的任何维护都是痛苦的。保持整个表允许用户回滚任何先前的更改,但这开始感觉有点矫枉过正。所以我在同步过程中添加了一个例程,如果历史表中不再存在设备上次下载的历史记录项,则服务器不会为其提供最近的历史记录项,而是为其提供包含所有数据的文件。那个帐户。然后我添加了一个cronjob来删除超过90天的历史记录项。这意味着用户仍然可以回滚不到90天的更改,如果他们每90天至少同步一次,则更新将像以前一样递增。但如果他们等待超过90天,该应用程序将替换整个数据库。

该更改将历史记录表的大小减少了近90%,因此现在维护历史记录表只会使数据库大两倍而不是十倍大。此系统的另一个好处是,如果需要,同步仍然可以在没有历史记录表的情况下工作 - 例如,如果我需要进行一些暂时脱机的维护。或者我可以为不同价位的帐户提供不同的回滚时间段。如果有超过90天的下载更改,则完整文件通常比增量格式更有效。

如果我今天重新开始,我会跳过ID冲突检查,只是针对足以消除冲突的密钥长度,以及某种错误检查以防万一。但历史表以及最近更新的增量下载组合或需要时的完整下载一直运行良好。

对于增量(更改)同步,您可以使用pubsub模式将更改发布回所有订阅的客户端,推送器等服务可以做到这一点。

对于数据库镜像,一些Web框架使用本地迷你数据库将服务器端数据库同步到浏览器数据库中的本地,支持部分同步。查看计量表

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top