如何实现双向事务复制

文章翻译 文章翻译
文章编号: 820675 - 查看本文应用于的产品
展开全部 | 关闭全部

本文内容

概要

本分步指南介绍了如何实现双向事务复制。本文还讨论了实施双向事务复制中所涉及的问题。

双向事务复制

双向事务复制也称为双向的事务复制允许一个服务器作为发布者和订阅服务器相同的数据。因为参与复制的服务器将的复制到其他服务器的任何更改的任何更改都不会传播回发到原始服务器中。

例如对于如果两个服务器 (服务器 A 和服务器 B) 服务器被认为是双向事务复制中,如果两个下列条件都为真:
  • 对表 T1,服务器所做的更改 a 复制到表 T1,在服务器 b。
  • 对服务器 B 上的表 T1 所做的更改将被复制到表 T1,在服务器 a。
因此,如果更改是从服务器 A 发出,该更改会被复制到服务器 B,但服务器 B 不会传播回 Server A.复制到相同的更改将使用环回检测机制,用来确定是否要发送所做的更改回原始服务器的分发服务器。

规划双向事务复制拓扑

对于双向事务复制其中一台服务器可以充当中心的订户和所有其他服务器订阅到中央订阅服务器。因此,在服务器上发起的任何更改将复制到中央订阅服务器上,然后中央订阅服务器上依次,所做的更改将复制到该组的所有其他服务器参与复制的。然而,环回检测机制借助分发服务器会停止从原始服务器传播更改。

例如对于如果三个服务器 (服务器 A,服务器 B 和服务器 C) 参与双向事务复制,并且服务器 A 是中央订阅服务器的发布者和订阅者维护在以下方面:
  • 服务器 A 发布到服务器 B 和服务器 c。
  • 服务器 A 订阅从服务器 B 和服务器 c。
  • 将发布到服务器 B,并从服务器只订阅 a。
  • 服务器 C 将作为发布和订阅从仅服务器 a。
因此,在服务器 B 上产生的任何更改会被复制到服务器 A 和服务器 c。

冲突在双向事务复制

参与复制的服务器上进行更改时, 所做的更改将被复制到其他参与的所有服务器。 在这种复制过程中可能会发生冲突,复制可能会失败。下面的列表描述了可能发生的冲突,您可以避免这些冲突的方法:
  • 如果您插入一条记录,以便将到表中的密钥对其中一台服务器,并且已具有相同的密钥的另一条记录存在于其他参与复制的服务器上复制不会传播到其他服务器所做的更改。

    建议的操作若要不必此问题请确保在每个参与复制的服务器上使用不同的密钥。这样做分配键来参与复制的每个服务器预先确定的的范围。您还可以在每个服务器上使用复合键。
  • 当您更新已被另一台服务器删除一个记录时,UPDATE 语句影响其中记录删除和复制将失败,错误在服务器上的零行。

    建议的操作若要不必此问题可以执行以下步骤之一:
    • 删除 @ @ ROWCOUNT 后实际的 UPDATE 语句,在更新自定义存储过程中检查。

      -或者-
    • 使用该 -Skiperrors 分发代理程序要跳过此错误的参数。有关事务复制中跳过错误的详细信息,请访问下面的 Microsoft 网站:
      http://msdn2.microsoft.com/en-us/library/aa178842(SQL.80).aspx
      -或者-
    • UPDATE 语句更新中的存储过程之前,请查找记录。如果没有记录绕过 UPDATE 语句,该记录已删除所有订阅服务器上。
  • 在更新中的另一台服务器上同时更新记录的列时, 数据可能是其他两个服务器上。

    建议的操作若要不必此问题确定是否其他的服务器上一次更新数据,然后采取任何必要的操作。若要执行此操作、 修改更新自定义存储的过程并使用 XCALL 语法来调用该更新的存储过程。XCALL 语法之前更新过程被调用,并提供了更新后的值列中,所有列提供值。调用更新存储过程之前,您可以比较的值与列的当前值。如果您看到不同的值,则列正在更新一次通过不同的服务器。您可以自定义存储的过程以选择哪个值仍然存在。有关如何使用 XCALL 的详细信息请访问下面的 Microsoft 网站:
    http://msdn2.microsoft.com/en-us/library/Aa237133(SQL.80).aspx
    注意您可以指定 XCALL 语法来调用相应的更新将存储过程,或通过在发布过程中使用 sp_addarticle 删除存储的过程。您也可以通过使用 SQL Server 企业管理器指定 XCALL 语法。这样做,请按照下列步骤操作:
    1. 在 SQL Server 企业管理器中找到所需的出版物。
    2. 用鼠标右键单击该的出版物,然后单击 属性
    3. 单击 项目 选项卡,查找文章,然后单击项目旁边的文章属性按钮 (...)。
    4. 表项目属性 对话框中单击 命令 选项卡。
    5. 替换 UPDATE 命令与此存储的过程调用 文本框中键入 XCALL,然后单击 确定
    6. 出版物属性 对话框中单击 确定
  • 在更新记录中的不同列时, 的一条记录的不同列的同时更新有时可能导致冲突。

    建议的操作若要不必此问题确定是否不同的列中相同的记录一次更新,然后采取任何必要的操作。 执行此操作、 修改更新自定义存储的过程,和然后使用该 XCALL 语法来调用该更新的存储过程。因为 XCALL 语法提供值的情况下,调用该存储的更新过程之前,您之前执行实际更新时可以将下列选项之一添加到存储的更新过程:
    • 将所有调用它们的值之前更新存储过程对列的当前值进行比较。
    • 添加称为表示行版本,并更新存储过程之前,比较其当前值与它的值的列。
    不同的值表示不同的列都在同一时间进行更新。然后,您可以决定是否要更新列。
  • 您在删除正在由另一台服务器在同一时间被更新的行时,复制可能会失败。

    建议的操作确定是否更新行时,并在 $ 删除中删除一次使用 XCALL 语法的存储过程。比较之前调用该删除存储过程正在删除对值的行的每一列。不同的值表示这些更新是在同一时间执行。您可以删除或保留更新的行。

    注意即使不会删除订阅服务器上的记录,记录来自 DELETE 的服务器上不再存在语句。
  • 您在删除在同一时间参与复制的另一台服务器上被删除的行时复制将失败,因为 DELETE 语句不会影响某些订阅服务器上的任何行。

    建议的操作若要不必此问题可以执行以下步骤之一:
    • 删除 @ @ ROWCOUNT 后实际的 DELETE 语句,在更新自定义存储过程中检查。

      -或者-
    • 使用该 -Skiperrors 分发代理程序要跳过此错误的参数。有关事务复制中跳过错误的详细信息,请访问下面的 Microsoft 网站:
      http://msdn2.microsoft.com/en-us/library/aa178842(SQL.80).aspx
注意每个部署中可能需要一种不同的方法解决具体取决于业务要求的这些冲突。这些冲突,则容易解决时涉及只有两台服务器。当两个以上服务器涉及到您可能能够使用存储的过程来确定哪个服务器发起所做的更改。用来更新服务器 C 中的记录将更新存储过程并不知道是否更改产生在服务器 A 或在服务器 b。与合并复制不同事务复制不旨在解决冲突。部署事务性复制只在方案中,而不是避免冲突解决。

实现双向事务复制

要执行双向事务复制,必须满足以下所有条件:
  • 在复制的服务器之间同步数据。
  • 所使用的复制存储的过程均在所有参与数据库中。
  • 该订阅已设置使用 @ loopback_detection = true 参数。

    注意选择 设置 @ loopback_detection = true 目前不在用户界面中。因此,您必须订阅使用 sp_addsubscription 存储过程。
如果您可以在执行备份和恢复,请记住不同站点需要不同的约束,以确保不会创建重复的主键的主键上。此外请记住使用 NOT FOR REPLICATION 子句创建的所有约束。

您可以使用下面的示例设置双向运行 SQL Server 2000 的计算机上的事务性复制。

注意以下步骤的每个列出的处理 SQL 语句。运行在上一步中执行任务所提及的 TRANSACT-SQL 语句。
  1. 运行 SQL Server 的计算机上创建分发服务器、 发行商,和订阅服务器。这样做,请按照下列步骤操作:
    1. 创建两个数据库:
      use master
      go
      
      create database test1
      go
      
      create database test2
      go
      
    2. 创建两个具有一个带有 NOT FOR REPLICATION 选项集的 标识 列的表:
      use test1
      go
      
      create table two_way_test1 
      (
      	pkcol		INTEGER PRIMARY KEY NOT NULL,
      	intcol		INTEGER IDENTITY(1,1) NOT FOR REPLICATION,
       	charcol		CHAR(100),
      	timestampcol 	TIMESTAMP
      )
      
      use test2
      go
      
      create table two_way_test2 
      (
      	pkcol 		INTEGER PRIMARY KEY NOT NULL,
      	intcol 		INTEGER IDENTITY(1000000000,1) NOT FOR REPLICATION,
       	charcol 	CHAR(100),
       	timestampcol 	TIMESTAMP
      )
      go
    3. 分配到主关键字列的值的预先确定的范围,以便在不同的服务器上的值不在相同位置的单元格区域中。例如对于可以强制使用 two_way_test1test1 数据库中的键的范围为 1-1000年,然后 two_way_test2test2 数据库中的表的键的范围为强制 1001年-2000年。若要这样做使用下面的代码:
      	-- Constraint to enforce a range of values between 1 and 1000 in database test1 
      use test1
      go
      
      alter table 
      	two_way_test1 
      with nocheck 
      add constraint 
      	checkprimcol check NOT FOR REPLICATION 
      	(
      		pkcol BETWEEN 1 and 1000
      	) 
      go 
      
      
      use test2
      go
      
      	-- Constraint to enforce a range of values between 1001 and 2000 in the database test2 
      
      alter table 
      	two_way_test2
      with nocheck 
      add constraint 
      	checkprimcol check NOT FOR REPLICATION 
      	(
      		pkcol BETWEEN 1001 and 2000
      	) 
      go 
      
      
  2. 启用您的服务器作为分发服务器,然后创建一个分发数据库:
    use master
    go
    sp_adddistributor @distributor = '<distributor name>' 
    go
    use master
    go
    sp_adddistributiondb @database='distribution'
    go
  3. 启用的所有计算机运行 SQL Server 复制作为发布者参与:
    use master
    go
    
    exec sp_adddistpublisher 
    	@publisher = '<Your Server Name>', 
    	@distribution_db ='distribution', 
    	@security_mode = 0, 
    	@login = 'sa', 
    	@password = 'sa',
    	@working_directory ='<Location of Directory>'
  4. 启用所有已标识的数据库都不能用于复制:
    use master
    go
    
    exec sp_replicationdboption N'test1', N'publish', true
    go
    
    exec sp_replicationdboption N'test2', N'publish', true
    go
    
  5. 为 INSERT,UPDATE,创建自定义存储的过程和删除操作,将在复制期间进行的更改应用的所有数据库。

    注意通常,到一个 IDENTITY 列中插入值时表的 IDENTITY_INSERT 选项必须为 ON。通过传入复制代理程序的 NOT FOR REPLICATION 选项来完成此任务。

    您将无法更新标识列中的值。因此时复制期间更新值,, 您必须删除旧的值,并插入新的值。若要创建自定义存储过程,请按照下列步骤:
    1. Create the custom stored procedures in the test1 database:
      use test1
      go
      
      -- INSERT Stored Procedure
      
      create procedure sp_ins_two_way_test1	
      	@pkcol int, 
      	@intcol int, 
      	@charcol char(100), 
      	@timestampcol timestamp,
      	@rowidcol uniqueidentifier
      as
      	insert into two_way_test1 
      	(
      		pkcol,
      		intcol,
      		charcol
      	) 
      	values
      	(
      		@pkcol, 
      		@intcol, 
      		@charcol
      	)
      go
      
      --UPDATE Stored Procedure
      
      create procedure sp_upd_two_way_test1	
      	@pkcol int, 
          	@intcol int, 
          	@charcol char(100), 
          	@timestampcol timestamp,
      	@rowidcol uniqueidentifier,
          	@old_pkcol int
      as
      	declare @x int
      	declare @y int
      	declare @z char(100)
       
      	select 
      		@x=pkcol, 
      		@y=intcol, 
      		@z=charcol 
      	from 
      		two_way_test1 
      	where 
      		pkcol = @pkcol
      
      
      
      	delete 
      		two_way_test1 
      	where 
      		pkcol=@pkcol
      
      
      
      	insert into two_way_test1 
      	(
      		pkcol, 
      		intcol, 
      		charcol
      	) 
      	values
      	(
      		case isnull(@pkcol,0) when 0 then @x else @pkcol end,
      		case isnull(@intcol,0) when 0 then @y else @intcol end,
      		case isnull(@charcol,'N') when 'N' then @z else @charcol end
      	)
      go
      
      --  DELETE Stored Procedure
      
      create procedure sp_del_two_way_test1 
      	@old_pkcol int
      as
      	delete 
      		two_way_test1 
      	where 
      		pkcol = @old_pkcol
      go
      
      
    2. Create the custom stored procedures in the test2 database:
      use test2
      go
      
      -- INSERT Stored Procedure
      
      create procedure sp_ins_two_way_test2	
      	@pkcol int, 
      	@intcol int, 
      	@charcol char(100), 
      	@timestampcol timestamp,
      	@rowidcol uniqueidentifier
      as
      	insert into two_way_test2 
      	(
      		pkcol,
      		intcol,
      		charcol
      	) 
      	values
      	(
      		@pkcol, 
      		@intcol, 
      		@charcol
      	)
      go
      
      --UPDATE Stored Procedure
      
      create procedure sp_upd_two_way_test2	
      	@pkcol int, 
          	@intcol int, 
          	@charcol char(100), 
          	@timestampcol timestamp,
      	@rowidcol uniqueidentifier,
          	@old_pkcol int
      as
      	declare @x int
      	declare @y int
      	declare @z char(100)
       
      	select 
      		@x=pkcol, 
      		@y=intcol, 
      		@z=charcol 
      	from 
      		two_way_test2 
      	where 
      		pkcol = @pkcol
      
      
      
      	delete 
      		two_way_test2 
      	where 
      		pkcol=@pkcol
      
      
      
      	insert into two_way_test2 
      	(
      		pkcol, 
      		intcol, 
      		charcol
      	) 
      	values
      	(
      		case isnull(@pkcol,0) when 0 then @x else @pkcol end,
      		case isnull(@intcol,0) when 0 then @y else @intcol end,
      		case isnull(@charcol,'N') when 'N' then @z else @charcol end
      	)
      go
      
      
      --  DELETE Stored Procedure
      
      create procedure sp_del_two_way_test2 
      	@old_pkcol int
      as
      	delete 
      		two_way_test2 
      	where 
      		pkcol = @old_pkcol
      go
      
  6. 创建某个事务发布,然后将项目添加到在出版物中 test1test2 数据库:
    --In the database test1.
    
    use test1
    go
    
    --  Adding the transactional publication.
    exec sp_addpublication 
    	@publication = N'two_way_pub_test1', 
    	@restricted = N'false',
    	@sync_method = N'native', 
    	@repl_freq = N'continuous',
    	@description = N'Transactional publication of database test1.',
    	@status = N'active', 
    	@allow_push = N'true', 
    	@allow_pull = N'true',
    	@allow_anonymous = N'false', 
    	@enabled_for_internet = N'false',
    	@independent_agent = N'false', 
    	@immediate_sync = N'false',
    	@allow_sync_tran = N'true', 
    	@autogen_sync_procs = N'true',
    	@retention = 72
    go
    
    exec sp_addpublication_snapshot 
    	@publication = N'two_way_pub_test1',
    	@frequency_type = 4,
    	@frequency_interval = 1, 
    	@frequency_relative_interval = 0,
    	@frequency_recurrence_factor = 1, 
    	@frequency_subday = 1,
    	@frequency_subday_interval = 0, 
    	@active_start_date = 0,
    	@active_end_date = 0, 
    	@active_start_time_of_day = 233000,
    	@active_end_time_of_day = 0
    go
    
    --  Adding the transactional articles.
    
    exec sp_addarticle 
    	@publication = N'two_way_pub_test1', 
    	@article = N'two_way_test1',
    	@source_owner = N'dbo', 
    	@source_object = N'two_way_test1',
    	@destination_table = N'two_way_test1', 
    	@type = N'logbased',
    	@creation_script = null, 
    	@description = null, 
    	@pre_creation_cmd = N'drop',
            @schema_option = 0x00000000000000F1, 
    	@status = 16,
    	@vertical_partition = N'false', 
    	@ins_cmd = N'CALL sp_ins_two_way_test2',
    	@del_cmd = N'CALL sp_del_two_way_test2',
    	@upd_cmd = N'CALL sp_upd_two_way_test2', 
    	@filter = null,
    	@sync_object = null
    go
    
    --  In the database test2
    use test2
    go
    
    --  Adding the transactional publication.
    
    exec sp_addpublication 
    	@publication = N'two_way_pub_test2', 
    	@restricted = N'false',
    	@sync_method = N'native', 
    	@repl_freq = N'continuous',
    	@description = N'Transactional publication of database test2',
    	@status = N'active', 
    	@allow_push = N'true', 
    	@allow_pull = N'true',
    	@allow_anonymous = N'false', 
    	@enabled_for_internet = N'false',
    	@independent_agent = N'false', 
    	@immediate_sync = N'false',
    	@allow_sync_tran = N'true', 
    	@autogen_sync_procs = N'true',
    	@retention = 72
    go
    
    exec sp_addpublication_snapshot 
    	@publication = N'two_way_pub_test2',
    	@frequency_type = 4,
    	@frequency_interval = 1, 
    	@frequency_relative_interval = 0,
    	@frequency_recurrence_factor = 1, 
    	@frequency_subday = 1,
    	@frequency_subday_interval = 0, 
    	@active_start_date = 0,
    	@active_end_date = 0, 
    	@active_start_time_of_day = 233000,
    	@active_end_time_of_day = 0
    go
    
    --  Adding the transactional articles.
    exec sp_addarticle 
    	@publication = N'two_way_pub_test2', 
    	@article = N'two_way_test2',
    	@source_owner = N'dbo', 
    	@source_object = N'two_way_test2',
    	@destination_table = N'two_way_test2', 
    	@type = N'logbased',
    	@creation_script = null, 
    	@description = null, 
    	@pre_creation_cmd = N'drop',
    	@schema_option = 0x00000000000000F1, 
    	@status = 16,
    	@vertical_partition = N'false', 
    	@ins_cmd = N'CALL sp_ins_two_way_test1',
    	@del_cmd = N'CALL sp_del_two_way_test1',
    	@upd_cmd = N'CALL sp_upd_two_way_test1', 
    	@filter = null,
    	@sync_object = null
    go
    
  7. 参与复制的所有服务器都启用为订阅服务器:
    exec sp_addsubscriber 
    	@subscriber = '<Your Server Name>', 
    	@login = '<login name>', 
    	@password = '<password>'
    go
    
    
  8. 标识该数据库中的某一个中央订阅服务器。 参与复制,以便对中央订阅服务器订阅的所有数据库和中央订阅服务器订阅到所有其他数据库的所有数据库中创建事务性订阅。

    例如对于在这种情况下 test1 数据库是中央订阅服务器。test2 数据库中的订阅在 test1 发布和订阅在 test2 发布 test1 数据库中创建事务性订阅。

    注意创建具有 LOOPBACK_DETECTION 选项处于启用状态的所有订阅。

    若要这样做使用下面的代码:
    --Adding the transactional subscription in test1.
    
    use test1
    go
    exec sp_addsubscription 
    	@publication = N'two_way_pub_test1', 
    	@article = N'all', 
    	@subscriber = '<Your Server Name>', 
    	@destination_db = N'test2', 
    	@sync_type = N'none', 
    	@status = N'active', 
    	@update_mode = N'sync tran', 
    	@loopback_detection = 'true'
    go
    
    -- Adding the transactional subscription in test2.
    
    use test2
    go
    exec sp_addsubscription 
    	@publication = N'two_way_pub_test2', 
    	@article = N'all', 
    	@subscriber = '<Your Server Name>', 
    	@destination_db = N'test1', 
    	@sync_type = N'none', 
    	@status = N'active', 
    	@update_mode = N'sync tran', 
    	@loopback_detection = 'true'
    go
    
注意还可以通过使用 sp_scriptpublicationcustomprocs 系统存储过程创建所有发布的自定义存储的的过程。有关 sp_scriptpublicationcustomprocs 系统存储过程的详细信息,请参阅"sp_scriptpublicationcustomprocs"主题中 SQL Server 2000 更新丛书联机。

注意SQL 查询分析器返回每个列只有 256 个字符。您可以更改此选项以最大允许值。



参考

有关更多的信息请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
320499如何手动同步复制订阅,通过使用备份或还原
299903FIX: sp_scriptpublicationcustomprocs 生成复制存储过程
327817INF: 使用将"-SkipErrors"分发代理程序中的参数慎重
有关实现 SQL Server 7.0 中的双向事务复制的其他信息请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
300164INF: 如何设置在发布服务器和与事务复制订阅服务器上的标识列
240235联机丛书中的错误:"实施分区、 双向、 事务性复制"示例包含的错误

属性

文章编号: 820675 - 最后修改: 2007年11月26日 - 修订: 1.9
这篇文章中的信息适用于:
  • Microsoft SQL Server 2000 标准版
  • Microsoft SQL Server 2000 64-bit Edition
关键字:?
kbmt kbreplication kbcode kbtsql kbhowtomaster KB820675 KbMtzh
机器翻译
注意:这篇文章是由无人工介入的微软自动的机器翻译软件翻译完成。微软很高兴能同时提供给您由人工翻译的和由机器翻译的文章, 以使您能使用您的语言访问所有的知识库文章。然而由机器翻译的文章并不总是完美的。它可能存在词汇,语法或文法的问题,就像是一个外国人在说中文时总是可能犯这样的错误。虽然我们经常升级机器翻译软件以提高翻译质量,但是我们不保证机器翻译的正确度,也不对由于内容的误译或者客户对它的错误使用所引起的任何直接的, 或间接的可能的问题负责。
点击这里察看该文章的英文版: 820675
Microsoft和/或其各供应商对于为任何目的而在本服务器上发布的文件及有关图形所含信息的适用性,不作任何声明。 所有该等文件及有关图形均"依样"提供,而不带任何性质的保证。Microsoft和/或其各供应商特此声明,对所有与该等信息有关的保证和条件不负任何责任,该等保证和条件包括关于适销性、符合特定用途、所有权和非侵权的所有默示保证和条件。在任何情况下,在由于使用或运行本服务器上的信息所引起的或与该等使用或运行有关的诉讼中,Microsoft和/或其各供应商就因丧失使用、数据或利润所导致的任何特别的、间接的、衍生性的损害或任何因使用而丧失所导致的之损害、数据或利润不负任何责任。

提供反馈

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com