本篇文章是SQL Server Replication系列的第八篇,详细内容请参考原文。
在这一系列的前几篇你已经学习了如何在多服务器环境中配置合并复制。这一篇将介绍合并代理并解释它在复制过程中扮演的角色。还会详细讨论冲突解决方案。
代理
这一系列的第五篇深入讲解了事务复制中的SQL代理作业和复制代理。合并复制也会引入大量作业。
合并复制中不包含日志读取器代理和分发代理两大组件。它们的作用/角色由合并代理替代。如果你想了解更多其他的作业请查看第五篇文章。
配置合并复制的主要组件:
->快照代理
->合并代理
->表和数据库触发器
图8.1显示了不同组件的整体情况
图8.1 合并复制代理和触发器
绿色箭头表示读取,红色箭头表示写入。合并代理存在于分发服务器或订阅服务器,取决于订阅模式。
快照代理
类似事务复制,合并复制中的快照代理用来生成快照数据(快照数据用于执行订阅数据库的初始化同步)。这不是初始化的唯一方法,但它是最方便的方法。
对比事务复制,合并复制中的快照代理有一处不同之处。In transactional replication the snapshot agent could do its processing in a “concurrent” way.并行过程意味着快照代理不会在发布项目上长时间持有锁。这项技术依赖于快照生成期间写入到发布数据库的日志文件中的标记。These markers are picked up by the log reader agent and then used by the distribution agent to figure out which of those changes between the markers need to be applied to the subscriber and which were picked up by the snapshot agent.
由于合并复制不再使用日志读取器代理,它不能使用这些标记,因此并行快照过程不是合并复制的选项。这意味着在生成快照期间快照代理需要在所有的发布表上持有共享锁。The lock needs to be taken on all tables for the entire time,否则无法保证事务的一致性。这意味着在此期间发布表不能进行更新,对任何发布表的更新将会被阻塞。
在第六篇的快照章节建议将快照生成时间安排在非高峰时间。前一段解释了这个建议的原因。
默认SQL Server为每一个发布创建一个新的SQL代理作业执行快照代理。这类作业的名称按照这种格式:<发布服务器>-<发布数据库>-<发布>-<序号>.
数据传送
事务复制中在初始化同步后数据传送由两个代理共同完成:日志读取器代理(负责监控发布项目的变更并记录到分发数据库中)和分发代理(负责应用变更到订阅服务器)。
在合并复制中的数据传送也有两部分,但是只有一个是由复制代理来实现的。
触发器
监控和记录发布项目的变更是由一系列触发器(添加在发布数据库中)完成的。因为合并复制中数据能够从发布到订阅,同时订阅也能到发布,因此在订阅数据库中也添加有相同的触发器。
每一个发布的表都有三个触发器。他们的命名规则是:MSmerge_???_6E172576BF56489CBE4466E74A524A08,末尾一串进制数是内部项目标识,"???"代表下面三个值:
->"del"--删除触发器
->"ins"--插入触发器
->"upd"--更新触发器
这些触发器负责记录每一个被应用于已发表的表中的数据的变化。同样还存在一组数据库触发器负责捕获发布项目的架构变更,这些触发器有MSmerge_tr_alterschemaonly、MSmerge_tr_altertable、 MSmerge_tr_altertrigger和MSmerge_tr_alterview.
复制元数据和有关更改的所有信息都存储在几个系统表中。所有都是以msmerge_开始命名。还有几个系统视图也遵循相同的命名模式。更多详细信息请参考Books Online.
合并代理
合并代理使用MSmerge触发器中的信息同步发布和订阅之间的变更。合并代理,类似其他复制代理,是一个与SQL Server服务分离的独立程序。
默认SQL Server为每一个订阅创建一个作业执行合并代理。这个作业在分发服务器(推送订阅)或订阅服务器(请求订阅)上执行。推送订阅合并代理作业的名称按照这种格式:<发布服务器>-<发布数据库>-<发布>-<订阅服务器>-<序号>
请求订阅合并代理作业的名称稍有不同:<发布服务器>-<发布数据库>-<发布>-<订阅服务器>-<订阅数据库>-<序号>
两种情况下都会生成一个序号防止名称冲突。
冲突
Merge replication allows data changes to be synchronized between the publisher and several subscribers.如果一个订阅服务器在一个发布表中更新了一行,下一次同步时这个订阅服务器上的变更会被复制到发布服务器。再下一次同步时它会被应用到其他订阅服务器。
这就有可能发生冲突。假设订阅服务器A修改一个用户的电话号码,订阅服务器B修改同一个用户的电话号码为不同的值。假设订阅服务器A先同步,它的更改会被发布服务器接收。后来订阅服务器B同步,就会检测到一个冲突。
现在有两种方式处理这种情况。一个是订阅服务器B重写已存在的变更,因此订阅服务器A的变更会丢失。另一个是订阅服务器B的变更忽略,因此它的变更会丢失。
How to influence what kind of changes are considered conflicts by SQL Server and how those conflicts are resolved will be covered in the following sections.
跟踪级别
When setting up merge replication you can choose between two options that influence how the merge agent compares concurrent changes to identify conflicts:
->行级跟踪
->列级跟踪
这个选项可以在每个表的属性对话框中设置。如图8.2所示:
图8.2 选择跟踪级别
你可以在发布向导的项目页面得到这项设置。实际上你也可以在对象资源管理器连接到发布服务器,打开复制->本地发布,右击你的发布选择属性。对话框中选择项目页,然后通过项目属性按钮打开项目属性对话框。
行级跟踪
行级跟踪是默认设置。它也是需要最少资源的设置。如果一行更新,合并触发器记录uniqueidentifier列的值。如果在一个同步中两边出现相同的uniqueidentifier值变更,就会检测到冲突并采取适当的措施。
这种跟踪只需将uniqueidentifier的值存储在变更日志中,因此只需少量的存储空间。但它可能将非冲突变更标记为冲突。假设用户A更新一个客户的电话号码,用户B更新同一个客户的电子邮箱。这两项变更并不是冲突变更。然而,它们的值却是存储在客户表中的同一行,行级跟踪会将这些变更标记为冲突,因为两个进程是修改同一行。
列级冲突
Column Level Tracking does not see changes to two different columns in the same row as a conflict, but in turn requires more resources.除了更新行的uniqueidentifier的值,列级跟踪还记录更新列的信息。
以上面场景为例,一个用户更新客户的电话号码,另一个用户更新同一客户的电子邮箱,额外的变更信息让合并代理正确识别这种情况不是冲突。
只有当两边在同一行的同一列进行变更时才会标记为冲突,并根据冲突解决方案处理。
逻辑记录
还有第三种选项允许你检测变更(基于逻辑记录)。在上面的例子,如果电话号码保存在CustomerPhone表,电子邮箱保存在CustomerEmail表,两个通过外键与客户表关联,行级跟踪和列级跟踪都检测不到冲突。SQL Server允许你声明这些表之间的关系在合并复制中为逻辑记录。如果你选择逻辑记录跟踪,这个场景就会被当作冲突。逻辑记录跟踪在SQL2005引入,但在SQL2008已过时。因此在新版本你不应该再使用这项功能。
冲突类型
最常见的冲突是,在一节点(发布端/订阅端)更新一行,同时在另一个节点更新它。这种类型的冲突称为Update-Update冲突。
如果在一个节点更新一行,在另一节点删除它,称为Update-Delete冲突。
如果应用到一个节点的变更不能应用到另一个节点,也会引起冲突,比如由于违反约束。这种类型的冲突称为冲突失败。
冲突失败可能由于多种原因发生,包括不匹配的约束定义,使用没有自动标识管理和触发器的标识列。完整列表请参考Books Online.
冲突解决程序
当检测到一个冲突,合并代理使用冲突解决程序决定保留哪一个变更。你可以针对每一个项目选择冲突解决程序。第六篇简单地介绍了如何选择冲突解决程序。当你为发布添加一个项目时,你可以通过点击"项目属性"按钮设置它的属性。打开的对话框包含有冲突解决程序,如图8.3所示:
图8.3 选择冲突解决程序
这里你可以选择默认冲突解决程序或其他已经注册到分发服务器上的冲突解决程序。你也可以选择交互式解决冲突。任何冲突解决程序会决定哪个版本获胜,意味着获胜的版本会应用到两边。没获胜的版本会记录到冲突表中用于后续查看。详细信息参考"查看冲突"章节。
默认冲突解决程序
在第七篇简单提到的"订阅类型"和"优先级"。默认冲突解决程序使用优先级决定哪一个版本的数据在冲突中获胜。
第一个发布服务器的优先级通常是100。每一个订阅你可以选择"客户端"或"服务器"订阅类型。对于"客户端"订阅类型,先与发布服务器同步者入选。对于"服务器"优先级是介于0(最低优先级)和99.99(最高优先级)之间的数字。All changes originating on a server type subscriber assume the priority of that subscriber.
服务器订阅可以将数据重新发布到其他订阅服务器。
Once a change has a priority assigned to it, that priority does not change again.
让我们来看一个例子。图8.4显示合并复制配置包含8个节点。
图8.4 合并复制配置例子
节点A是第一个发布服务器(优先级100)。节点C是A的一个"服务器"类型的订阅(优先级50)。它重新发布给其他三个订阅服务器。节点B和节点H是"客户端"类型的订阅,其他节点都是"服务器"类型的订阅。
在这样的拓扑结构中,你必须运行多次同步步骤才能将变更应用到每一个节点。假设节点F发生一个变更。为了分发这个变更,首先F需要和C同步,然后C和A、G、H同步,最后A和B、D、E同步。如果在D和原始变更(F)之间检测到一个冲突,D节点的变更会获胜,因为它有更高的优先级。这样A必须重新同步B、C、E,然后C再重新同步它自己的订阅(F、G、H)。在这种情况下,需要五次同步才能完全同步变更。
Keep this in mind when you design a complicated layout of merge replication participants.
这一章节剩余篇幅涉及的"同步"均指完整的同步,包括所有必要的步骤同步所有节点。
表8.1罗列了图8.4(所有项目使用默认冲突解决程序)中两个节点在同步中发生冲突时哪一个会获胜。它假设变更发生在同步开始之前。
Nodes with a change | Result | |
A | Any other node | A wins |
B | D | The first node that synchronizes with A wins. |
D | E | D wins |
D | G | G wins |
F | E | E wins |
H | E | The first node that synchronizes with C wins |
H | B | The first node that synchronizes with A wins |
表8.1 图8.4拓扑图中的同步优先级
简而言之,规则如下:
->主发布服务器的变更总是赢
->如果同步前发布服务器也有变更,"客户端"订阅节点的变更将丢失。对于这一点,不管是发布服务器直接变更,还是由于其他节点同步过来的。
->A change to a client type node that got synchronized with its unchanged publisher will be treated as if it originated on that same publisher.
->Of two changes to server type nodes the one that was applied to the node with the higher priority wins, independent of the type and number of nodes between them and independent of the order of synchronization steps.
自定义冲突解决程序
自定义冲突解决程序允许你更改用于决定冲突幸存的规则。他们需要被分发服务器注册。关于如何创建自定义冲突解决程序和在分发服务器注册请参考Books Online.SQL Server安装时会有几个冲突解决程序。They range from date dependent resolvers to resolvers that use the maximum or minimum value of the conflicting values or that build an average of the conflicting values.图8.3显示了完整列表,详细信息参考Books Online.
业务逻辑处理程序
另一种影响冲突结果的方式是业务逻辑处理程序(BLH)的使用。BLH是用托管代码编写的组件。They get installed the same way as custom resolvers but allow for a lot more flexibility.他们可以在订阅服务器或分发服务器上执行,能够对一长串事件作出反应,包括无冲突的数据变更。为更新注册的BLH针对每一行的更新都会执行。执行期间BLH可以拒绝变更,解决冲突,甚至修改数值。更多详细内容参考Books Online.
交互式解决冲突
你可以使用同步管理器窗口交互式解决冲突。同步管理器窗口需要在订阅服务器上执行,并且需要是请求订阅。同步管理器功能对话框看起来类似下面章节中的冲突查看器。对话框会显示每一个遇到的冲突,并允许你手动选择冲突解决的结果。如果你要使用交互式解决冲突你需要在订阅服务器属性启用它。在它启用之后,你需要禁用所有已有的同步计划。如果不使用同步管理器执行同步操作,则将应用标准冲突解决规则,并在同步过程中看不到这些冲突。
查看冲突
所有的解决冲突都被合并代理记录。你可以之后查看它们,甚至修改它们的结果。为了查看冲突,对象资源管理器下连接到发布服务器,打开复制->本地发布,右击发布选择"查看冲突",如图8.5所示:
图8.5 查看冲突
打开的对话框允许你选择你想查看冲突的表。只有那些遇到冲突的表才会显示,后面会有发生冲突的次数。图8.6显示这样的对话框:
图8.6 选择冲突的表
在你选择一个表之后,就会打开复制冲突查看器(图8.7)。上半部分包含所选表上发生的所有冲突列表。这个列表显示了冲突解决落选方、冲突类型、日期。
图8.7 冲突查看器
底部你可以看到入选方和落选方的详细列值。你可以从列表中移除一个或多个冲突(从列表移除条目不会影响复制表中的数据)。你也可以为每一个冲突提交新的入选方。要这样做请点击底部的"提交入选方"或"提交落选方"按钮。
提交一个新的入选方将会删除冲突列表的条目,并将以新值更新发布服务器。在下一次同步时会从发布将变更分发到其他节点。
总结
这一篇讲述了合并复制的工作机制。介绍了快照代理和合并代理,讨论了它们在合并复制过程中的角色/作用。之后描述不同的冲突解决程序。最后介绍冲突查看器,并解释如何使用它影响冲突解决过程的结果。