Microsoft Sync Framework基础篇 3:Microsoft Sync Framework元数据和同步流程
Microsoft Sync Framework元数据和同步流程
元数据(Metadata)
Microsoft Sync Framework为脱机和协作的应用程序、数据存储和设备提供了一个完整的同步平台,而不用考虑如下限制:
- 同步的数据类型
- 数据存储的类型
- 传输协议
- 网络拓扑比如点对点或客户端-服务器拓扑
相反,Sync Framework通过一个通用的元数据模型来允许Sync Framework完成下面的工作:
- 实现同步过程的互操作性
- 减少在两个参与同步的data stores之间的数据传输量
- 使同步独立于任何网络拓扑、数据类型、数据存储和传输协议
在本篇博客中,我们将详细的了解和学习通用的元数据模型以及它的组件,当然,我们也会讨论Sync Framework如何使用元数据来同步不同的数据存储和副本。
什么是元数据
从字面意义上看,元数据是“关于数据的数据”。而Microsoft Sync Framework中使用的元数据包含两种类型:
- 副本元数据(Replica metadata)
- 项目元数据(Item metadata)
在Sync Framework中,副本通常是指真正的数据存储。比如,如果我们在同步两个数据库,那么每个数据库都是一个副本,副本可以包含项目。比如,对于数据库,一个项目可以是表中的一条记录。
要同步两个副本, Sync Framework要求同步提供程序使用通用的元数据模型,这也是Sync Framework的核心所在。Sync Framework提供程序使用元数据来检测副本的更新,但是提供程序本身并不要求理解同步元数据(这是Sync Framework运行时的职责所在,它会帮助提供程序来解析同步元数据)。
同步提供程序通过运行时来查询元数据,以发现某个副本自上一次同步以来所作的数据更新。同步元数据还帮助完成对冲突的检测和处理。冲突是指在同步过程中,同一项目在两个副本中被同时修改。同步提供程序使用元数据来判断一个项目是否处于冲突状态并使用元数据来解决冲突。同步元数据还会被Sync Framework运行时使用,以解决通常的同步问题比如网络失败、冲突数据和应用程序错误。
元数据存储(Metadata Store)
你可能会对元数据存放在何处感到好奇,实际上,元数据可以存放在任何地方:一个文件、一个单独的数据库或参与同步的副本上。我们唯一需要保证的就是该元数据存储是可以通过编程来进行访问的。Sync Framework通过调用同步提供程序的不同方法来获取和更新元数据。在实现自己的元数据存储时,我们需要在相应方法被执行时返回和修改我们的元数据。
但多数情况下,我们会把对元数据进行操作的任务托管给Sync Framework运行时,这是因为Microsoft Sync Framework 提供一个以 SQL Server Compact Edition 为基础的完整元数据存储实现,如下图所示。该存储并非必要,但使用它意味着您不必担心如何存储同步元数据。
使用内置的元数据存储还是自定义的元数据存储,取决于创建同步程序的开发人员。但使用内置的元数据存储意味着您不必担心如何存储同步元数据。
同步元数据组件
用于数据存储的元数据可以分为三个主要组件:
- 版本(Version)
- 知识(Knowledge)
- Tombstones
版本(Version)
同步版本和副本中的项目相关联:该信息记录了项目在何时何处被创建和发生的变化,以及与该项目关联的项目ID。比如在数据库同步中,同步的数据库可以成为副本A和副本B。一个项目可能是数据库中的一个表,或表中的以条记录,甚至是表中某行的一列。
当项目发生更改时,存储的有关该更改的信息将包括创建版本和更新版本。这些版本包含两个组件:
- 滴答计数 :它是一个在整个源副本范围内使用以唯一标识一个更改的逻辑时钟
- 副本ID:它用于唯一标识发生更改的数据存储。
当首次创建项目时,创建版本与更新版本相同。对该项目的后续更新修改的只是更新版本。
举个例子,假如参与同步的副本A中Customer表的数据如下:
假设我们使用表中ID字段作为项目ID,那么表中记录的版本信息可以用下表来表示:
记录项目版本的过程也叫更改跟踪(change tracking)。要实现更改跟踪,同步提供程序需要为副本中任何时候的修改项目更新其同步版本。更改跟踪(版本)的实现主要有两种方式:
- 内联跟踪:
在这种方法中,版本的修改在副本中的项目被修改时发生。这种方法通常用在我们可以将修改版本信息的方法嵌入到副本本身时,以数据库为例,我们可以使用触发器在更新行之后立即更新更改跟踪表。
- 异步跟踪:
在这种方法中,将运行外部进程来扫描更改。发现的任何更新将添加到版本信息当中。该进程可能是定期执行进程的一部分,或者它可能在同步之前执行。该进程通常用于当没有内部机制能够在项目更新时自动更新版本信息的情况中(例如,无法在更新流程中增加版本更新逻辑)。检查更改的常用方式是存储项目的状态,并将存储的状态与项目当前状态进行比较。例如,可检查从上次同步起,最后写入时间或文件大小是否发生变化。
知识(Knowledge)
知识是副本能够感知的数据更改的精简表示。版本和项目相关联,而知识则是和sync scope(同步作用域)相关联。知识包含了在一个副本上直接或间接所作修改的信息。同步提供程序通常不会直接使用知识,取而代之的是,Sync Framework运行时会调用提供者的方法来操作副本的知识。知识的目的在于使同步更加有效,因为它有助于限制在副本之间发送的信息量。当版本信息更新时,用于数据存储的知识也随之更新。
提供程序使用副本知识的目的有:
- 枚举更改:确定另一个副本没有感知的更改。
- 检测冲突:确定哪项操作是在不了解彼此知识的情况下做出的。
一个副本的知识由副本ID和副本中最大的滴答计数组合而成,以上面的数据库同步为例,包含Customer表的副本A的知识为A2。
Tombstones
每个副本还必须为每个删除的项目维护tombstone 信息。如果不跟踪删除信息,提供程序将无法告知某个项目(如文件)已被删除。在这种情况下,提供程序无法将更改版本信息发送至其他提供程序。Tombstone 必须包含以下信息:
- 全局 ID: 用于在所有副本中唯一确定 tombstone 项目的副本 ID 和滴答计数。
- 删除版本: 与 tombstone 项目关联的更新版本
- 创建版本: 最初创建项目时关联的副本 ID 和滴答计数
因为 tombstone 日志中的信息将随时间增加,所以有必要创建一个进程定期清理该存储。清理 tombstone 数据能够节省空间并且有助于改善同步性能。Microsoft Sync Framework 支持对tombstone 信息的管理。
同步流程
了解了同步元数据,我们就可以来学习同步流程了。发起同步的副本称为源 而源所连接的副本称为目标。本文接下来的部分将介绍下图所示的同步流程。对于双向同步,将执行此进程两次,第二次迭代时会交换源和目标。
1. 发起同步会话
在这一阶段将建立同步会话,从而创建了从源到提供程序的链接。
2. 目标准备并发送知识
如前所述,每个副本都会存储其自身的知识。存储在目标端的知识将传递到源。
3. 目标知识用于确定要发送的更改
在源端,会将刚刚收到的知识与本地项目版本进行比较,以确定目标端尚不了解的项目。值得注意的是,发送的版本并不是实际的项目,而是每个项目上次发生更改的位置摘要。
4. 更改版本和源知识发往目标端
当源准备好所需的更改版本列表之后,这些版本将传输到目标端
5. 检索更改项目的本地版本并与源版本和知识进行比较
目标端使用这些版本准备源需要发送的项目列表。目标还使用该信息检测是否存在限制冲突。限制冲突是指违反了项目限制,如文件夹关系或文件系统中同名数据的位置。
6. 检测并解决或推迟冲突
基本上,如果在两次同步期间对两个副本上的相同项目进行更改就会发生冲突。在 Microsoft Sync Framework 运行时中,当其中一个副本的更改版本不包含另一个副本更改的知识时便会检测到冲突。 将在下面的“冲突示例”部分中介绍了说明该检测过程如何工作的更加详细的示例。
副本可以自由实施各种策略,解决同步拓扑间发生冲突的项目。下面列举了一些常用的冲突解决策略:
- 源获胜: 当检测到冲突时,总是采用源副本所做的更改。
- 目标获胜: 总是采用目标副本所做的更改。
- 合并: 将源副本和目标副本所做的更改合并在一起。库存统计可能是一个您希望将两个副本的值合并(求和),而不是选取其中一个作为正确值的例子。
- 记录冲突: 记录或推迟冲突。
7. 目标向源请求项目数据
在这一阶段,目标已经确定需要在源中检索的项目,并将请求发送到源。
8. 源准备并发送项目数据
源接收到项目数据请求,并准备要传输到目标的实际数据。如果要跟踪的项目是数据库中的一行,则将发送该行。如果项目是文件夹中的文件,则将传送该文件。
9. 项目应用到目标中
目标接收并应用项目。如果在此过程中出现任何错误(如网络断开),则该项目将被标记为异常,并在下次同步期间进行更正。从源接收的知识将添加到目标知识。
同步示例
通过使用前面介绍的同步流程,我们将实际操作一次文件同步示例,该示例将说明 Microsoft Sync Framework 如何通过元数据来枚举更改并最终应用项目数据。在本例中有两个副本:副本 A 和副本 B。副本 A 启动与副本 B 的同步(即副本 A 是源而副本 B 是目标)。假定我们希望同步两个副本间的文件。要跟踪的项目是文件夹中的一个文件,表示为 In (例如,I1, I2, I3…)。创建新文件时 (I1) 与该文件相关联的元数据应更新如下:
如果文件再次更新,版本表应如下所示:
在上述示例中,更新滴答计数设置为 5,这是由于用于滴答计数的逻辑时钟在整个源内发挥作用,即:滴答计数 2-4 已用于副本中其他项目的更改。
例如,在下面的图例中,跟踪的副本中有两个附加的项目 I2 和 I3 。您可以看到,随着创建更多的项目,版本信息将变得越来越多。Microsoft Sync Framework 不要求存储以前的更新版本。它只需要了解最新的更新版本。
如果采用该副本的当前项目状态,我们可以把副本 A 的知识表示为:
副本 A 知识 = A5
如前所述,知识是副本能够感知的数据更改的简约表述。在本例中,A 是分配给该副本的唯一 ID,而 5 是当前滴答计数,它使副本能够了解当前的最大变化个数。如果该副本已经与任意其他副本进行同步,则我们还将在该列表中看到这一知识。
在副本 B 上可能也有很多文件。该副本如下所示:
副本 B
副本 B 的当前知识为:
副本 B 知识 = B4
此时我们选择开始在两个副本之间进行同步。副本 A 将成为源(启动同步的副本),副本 B 将成为目标。
同步过程中,目标向源发送其知识。如前所述,两个副本的知识如下:
副本 A 知识 = A5
副本 B 知识 = B4
源(副本 A)收到该知识并使用它来确定将哪个版本发送到目标。由于副本 B 不了解在副本 A 中的任何项目,所以它将发送所有内容。在本例中,副本 A 将包含以下版本。
副本 A 的更改批次
目标接收这些版本并对其进行枚举以确定需要从源请求哪些项目。它还使用该信息确定是否存在任何冲突(例如,在两个副本上更新了相同的文件)。
完成后,目标请求源发送它没有感知的项目。在本例中,副本 A 将发送与 I1、 I2 和 I3。
目标收到这些文件并将其添加到自己的文件夹中。副本 B 的项目现在将包含从副本 A 接收到的项目。
副本 B – 已更新项目表
本次同步结束后,该过程将再执行一遍,这次源将成为目标而目标成为源。这使得副本 A 能够接收到在副本 B 上创建或更改的任何文件(I104 和 I105)。
同步完成后,两个副本上都应包含以下更新知识。
副本 A 知识 = A5, B4
副本 B 知识 = A5, B4
冲突示例
继续前面的示例,两个副本现在已经“同步”,并且每个项目标版本如下:
类似地,两个副本的知识如下:
副本 A 知识 = A5, B4
副本 B 知识 = A5, B4
此时,两个副本都决定更新相同的文件(项目 I2)。
在副本 A 上,该项目标版本表更新为:
在副本 B 上,该项目标版本表更新为:
两个副本的知识也更新为:
副本 A 知识 = A6, B4
副本 B 知识 = A5, B5
此时,副本 A 启动与副本 B 的同步。跳过源向目标发送项目版本和知识这一步,为项目 I2 执行下列步骤。
1. 副本 B 看到项目 I2 新更改,其为:
2. 副本 B 查看从副本 A 收到的知识(A6、B4)并确定副本 A 不了解由副本 B 对相同项目所做的更改:
3. 将检测到冲突并传给应用程序或提供程序进行处理。
如前所述,应用程序能够选择如何处理冲突或延后处理。如果冲突延后处理,则在其解决之前它将在每次同步时重复出现。一旦冲突得到解决,则下一次同步时,原始副本将接收更新后的值。
在系列博客的第一部分基础篇中,通过3篇文章我们了解了同步的背景知识、同步的优点以及Microsoft Sync Framework对同步的实现;讲述了Sync Framework的核心组件和系统架构、各种同步参与方类型;详细的讨论了Sync Framework元数据的作用、同步流程,并实例展示了运行时是如何使用元数据来完成同步的。至此,我们对于Sync Framework的理论基础应该有了一个比较好的理解和掌握,接下去,我们将会详细的讨论使用具体的同步提供程序(Synchronization Providers)来同步各种数据,如数据库、文件、Web Feeds...