积极答复者
SQL 2008 R2与SQL 2014之间,使用复制功能,复制的数据不正确.

问题
-
三台服务器,使用SQL的复制(事务发布)功能来复制表.
环境
- A服务器: SQL 2008 R2 (Microsoft SQL Server Enterprise Edition (64-bit))
- 版本:10.50.6220.0 (刚刚打过SP3补丁)
- 操作系统 : 6.1.7601
- B服务器:SQL 2014 (Microsoft SQL Server Enterprise (64-bit))
- 版本:12.0.4213.0(打过SP1补丁)
- 操作系统:Microsoft Windows NT 6.3 (9600)
- C服务器:SQL 2014 (Microsoft SQL Server Enterprise (64-bit))
- 版本:12.0.4213.0(打过SP1补丁)
- 操作系统:Microsoft Windows NT 6.3 (9600)
备注:B和C服务器硬件和软件配置相同.
SQL复制设置
- B和C服务器中都有表T,使用B和C的SQL复制功能中的本地发布,发布表T,发布表T的设置中,都为默认设置,只有"目标对象名称"更改过,B服务器的设置内容为"T_B",C服务器的设置内容为"T_C".(因为B和C都复制各自的T到A中,为了A中表不重名,所以B和C发布的表T都改了目标对象名称)
- A服务器分别设置B和C的本地订阅.
问题
- A服务器中的表T_B和T_C经常会比发布源的B和C服务器中的表T的数据条数要多,发现是B的某些数据复制到了表T_C中,C的数据也有些复制到了T_B中.
- A服务器中的表T_B和T_C经常会比发布源的B和C服务器中的表T的数据条数要少一些.
查看复制监视器,没有任何错误.
刚刚创建本地订阅或者重新初始化所有订阅后,数据是正确的,但过段时间(1小时以内),数据条数就不正确了
现在我每次要读取A服务器的数据的时候,只能操作B和C的SQL中的复制->本地发布->对应的发布名->右键->重新初始化所有订阅->使用新快照 这个方法来使用.我都要崩溃了,呵呵.求教MS大神,这个有什么解决方法?
- 已编辑 MaskJeff 2015年8月24日 5:51
答案
-
原因:
当A本地订阅时,会在A上建立三个存储过程在订阅库的存储过程集中.分别是
- sp_MSins_dbo表名
- sp_MSupd_dbo表名
- sp_MSdel_dbo表名
而问题就出在这,这三个存储过程的默认名字没有服务器名,没有库名.只有表名.当我的A本地订阅B时,在A服自动创建了这三个SP.当我再次用A订阅C时,SQL再次创建了这三个同名但内容不同的SP(原SP覆盖掉了)!这样B和C向A复制数据时,都调用了最后一次进行订阅操作的C的SP,也就是B和C都对T_C进行复制操作!!
避免方法,在B和C创建本地发布时,发布表的属性中,要把最下面"语句传递"中的INSERT存储过程,UPDATE存储过程,DELETE存储过程的默认属性值改名.这样来避免A订阅时,因为SP名重复而产生的问题.
我觉得这应该算是SQL的一个BUG吧?看SQL的本地订阅列表就能看到,在创建本地订阅时,SQL会把订阅的名称按 "本地库名 - 源服务器名.库名:表"名来设置([POS] - [SVR001].[POS]:T_C),也就是说MS其实意识到订阅不同服务器时同名表的问题,那么在发布复制时,为什么不把这3个SP的命名规则也按这个来处理呢?或者表名使用"目标对像名称"的T_B,T_C组成sp_MSins_dboT_X也不会出现问题,又或者当A订阅B后再次订阅C时有个提示....完全静默的处理了......本地订阅的A服在建立本地订阅时,只可以选择订阅的库,其它属性不能设置(也许是我没找到).
假如A,B,C是不同的公司,在A订阅时,是看不到发布服务器做了怎样的设置,A在不了解的情况下很容易出现我遇到的这个问题.
解决方法 要不让B或者C改发布属性,要不就B和C的表放在不同的库中.
摘录sp_MSins_dboT的内容:
- A第一次订阅B时
ALTER procedure [dbo].[sp_MSins_dboT]
...
begin
insert into [dbo].[T_B](...
- A第二次订阅C时
ALTER procedure [dbo].[sp_MSins_dboT]
...
begin
insert into [dbo].[T_C](...
当第二次订阅C时,原sp_MSins_dboT的内容被新的C的sp_MSins_dboT覆盖.
- 已编辑 MaskJeff 2015年8月28日 7:15
- 已标记为答案 Nan YuMicrosoft contingent staff, Moderator 2015年9月4日 2:09
全部回复
-
我确认了一下数据.
现在A服务器中T_C表中比C服务器中多了几条数据.我查了一下,多的这几条数据,确实是B服务器中的表T中的数据,但不知道为什么,B服中表T大部分的数据都正常复制到了A服的T_B中,只有几条复制到了T_C中.
程序上应该没有问题,因为A服务器只是访问数据,没有UPDATE等操作.而且就算有其它的问题,那么T_C中多了这几条数据,T_B也应该有,因为毕竟是从B服务器过来的.
现状:
B服T表中主键的值
1
2
3
4
5
C服T表中主键的值
101
102
103
104
105
正确的情况应该是复制到A服后,表T_B的数据应该为
1
2
3
4
5
表T_C的数据应该为
101
102
103
104
105
但现在是,A服的T_B为
1
2
3
4
A服的T_C为
101
102
103
104
105
5 <- 5不应该出现在T_C中,而且为什么T_B中没有5 ?
-
继续报告测试状况.
今天早上再次确认,A服只订阅B服的情况下,复制的数据完全没有问题.
出问题时的SQL复制的设置情况:
- B服的表T本地发布(事务发布) -> 发布属性中,表T的"目标对象名称"为 T_B -> A服本地订阅B服的复制(T_B)
- C服的表T本地发布(事务发布) -> 发布属性中,表T的"目标对象名称"为 T_C -> A服本地订阅C服的复制(T_C)
问题现象:
A服同时订阅B的表T和C的表T的时候,数据确实有污染的情况,B服中表T的数据偶尔会有几条更新到了T_C中.反之同样C也复制到了T_B中,已经排除了触发器,或者其它程序更新了表T_B,T_C等原因.并且在B服复制监视器中,捕捉到了一条出错信息"违反了 PRIMARY KEY 约束“PK_T”。不能在对象“dbo.T_C”中插入重复键。重复键值为 (115602)。".确实是B向A事务复制时向T_C插入了数据.
这是SQL的BUG么?
-
B服务器捕捉到错误,错误提示是插入 T_C 的, 也就是你的应该同步到 T_B, 出错的是提示的T_C 的表?
如果是这样的话,出错的时候,根据错误信息中的 xact seqno, 在分发服务器分发库下通过 sp_browsereplcmds 查下复制的命令是什么, 如果是调用的存储过程,你再在订阅服务器订阅数据库查下对应存储过程写的表是否有问题
为了正常工作,已经关闭了A与C间的复制,只保留了A与B..现在暂时没有出错信息.
可以确定的是,事务复制的INS,DEL,UPD这三个都是调用复制功能自建的存储过程,而且理论上表名也没有问题,因为大部分的数据还是正确的进入了自己对应的表,只是小部分会有问题.
-
老是猜测问题在哪里,不如 trigger,看看到底谁动了问题数据
建议你还是弄个 trigger, 把每次操作的 iserted/deleted 和 @@spid 对应的 sysprocess 和 dbcc inputbuffer 的数据都存起来,出异常的时候,根据主键查对应的 sysprocess 和 dbcc inputbuffer 数据
不过复制异常报错的表名是另一个表,如果没有触发器,那应该是复制存储过程有问题了
恩,看来要查出明确的错误原因,只能用你的方法了.
现在为了保证工作,关闭了A与C的复制,暂时没有再出现错误.等我有机会再测试一下.
-
原因:
当A本地订阅时,会在A上建立三个存储过程在订阅库的存储过程集中.分别是
- sp_MSins_dbo表名
- sp_MSupd_dbo表名
- sp_MSdel_dbo表名
而问题就出在这,这三个存储过程的默认名字没有服务器名,没有库名.只有表名.当我的A本地订阅B时,在A服自动创建了这三个SP.当我再次用A订阅C时,SQL再次创建了这三个同名但内容不同的SP(原SP覆盖掉了)!这样B和C向A复制数据时,都调用了最后一次进行订阅操作的C的SP,也就是B和C都对T_C进行复制操作!!
避免方法,在B和C创建本地发布时,发布表的属性中,要把最下面"语句传递"中的INSERT存储过程,UPDATE存储过程,DELETE存储过程的默认属性值改名.这样来避免A订阅时,因为SP名重复而产生的问题.
我觉得这应该算是SQL的一个BUG吧?看SQL的本地订阅列表就能看到,在创建本地订阅时,SQL会把订阅的名称按 "本地库名 - 源服务器名.库名:表"名来设置([POS] - [SVR001].[POS]:T_C),也就是说MS其实意识到订阅不同服务器时同名表的问题,那么在发布复制时,为什么不把这3个SP的命名规则也按这个来处理呢?或者表名使用"目标对像名称"的T_B,T_C组成sp_MSins_dboT_X也不会出现问题,又或者当A订阅B后再次订阅C时有个提示....完全静默的处理了......本地订阅的A服在建立本地订阅时,只可以选择订阅的库,其它属性不能设置(也许是我没找到).
假如A,B,C是不同的公司,在A订阅时,是看不到发布服务器做了怎样的设置,A在不了解的情况下很容易出现我遇到的这个问题.
解决方法 要不让B或者C改发布属性,要不就B和C的表放在不同的库中.
摘录sp_MSins_dboT的内容:
- A第一次订阅B时
ALTER procedure [dbo].[sp_MSins_dboT]
...
begin
insert into [dbo].[T_B](...
- A第二次订阅C时
ALTER procedure [dbo].[sp_MSins_dboT]
...
begin
insert into [dbo].[T_C](...
当第二次订阅C时,原sp_MSins_dboT的内容被新的C的sp_MSins_dboT覆盖.
- 已编辑 MaskJeff 2015年8月28日 7:15
- 已标记为答案 Nan YuMicrosoft contingent staff, Moderator 2015年9月4日 2:09