随笔 - 229  文章 - 2  评论 - 507 

事务复制中的msrepl_ccs

在事务复制里,如果一个article被更新,distributionagent会调用相应的存储过程将数据更新到订阅端。 这些存储过程分别是[sp_MSins_dboTableName][sp_MSdel_dboTableName] [sp_MSupd_dboTableName] 分别对应插入,删除和更新操作。这些存储过程是在快照初始化时创建的。不过您可能曾经观察到还有其它两个存储过程被调用过:sp_MSins_dboTableName_msrepl_ccs,sp_MSdel_dboTableName_msrepl_ccs

那么两个存储过程的作用是什么呢?

   

sp_addpublication 的参数sync_method值为concurrent时,生成快照时article是允许被更新的,这些更新随后也会被应用到订阅端。 如果是使用默认的命令去更新,那么就可能遇到下面的情况:

假设表ta有2行数据,snapshot agent已经读取第一行和第二行数据,这时插入了第三条数据。接下来snapshot会去读第三条数据,并将三行数据打包到bcp文件中。而第三条数据的日志也会被logreader传递到分发。 这样就会产生一个问题:如果仍然使用sp_MSins_dboTableName,就会造成主键冲突,出现1033错误:Violation of %ls constraint '%.*ls'. Cannot insert duplicate key in object '%.*ls'. The duplicate key value is %ls. 

   

为了避免这种情况,设计出了另外两种存储过程(存储过程的定义请见文章结尾):

当操作是更新时,先判断改行是否存在,如果存在,则使用更新操作。

如果是删除操作,即使影响行为0,也不会抛出20598错误The row was not found at the Subscriber when applying the replicated command

如果是更新操作,则会先调用css的删除操作,然后调用css的插入命令,以避免20598错误。

所以在初始化的阶段,你会看到多出了两种存储过程。不过在初始化完成后,这两个存储过程就会从订阅段删除掉。

   

下面列出了三个命令的调用截图。 请注意,这类msrepl_ccs的类型有别于普通的存储过程,其值为-2147483618。

   

插入操作。sp_MSins_dboTableName会被解释成sp_MSins_dboTableName_msrepl_ccs

   

   

更新操作。sp_MSupd_dboTableName会分解为两个操作,现将数据删除,然后插入新的数据。

   

   

   

   

 删除操作。sp_MSdel_dboTableName会被解释成sp_MSdel_dboTableName_msrepl_ccs

   

   

存储过程的定义

create procedure [dbo].[sp_MSins_dbota_msrepl_ccs]

@c1 int,

@c2 int

as

begin

if exists (select *

             from [dbo].[ta]

            where [id] = @c1)---------在普通的存储过程中,是没有这段判断逻辑的。

begin

update [dbo].[ta] set

[c] = @c2

where [id] = @c1

end

else

begin

insert into [dbo].[ta](

[id],

[c]

) values (

    @c1,

    @c2    )

end

end

go

create procedure [dbo].[sp_MSdel_dbota_msrepl_ccs]

@pkc1 int

as

begin 

delete [dbo].[ta]

where [id] = @pkc1

---------在普通的存储过程中,还有一段额外的逻辑: 如果影响行为0,则抛出异常。

end 

go

   

   

   

   

一段删除的记录

   

posted on 2013-07-31 15:02  stswordman  阅读(389)  评论(4编辑  收藏