SQL Server复制系列3 - 存储过程sp_MSins_dboTableName_msrepl_ccs & sp_MSdel_dboTableName_msrepl_ccs的作用

在事务复制里,如果一个article被更新,distribution agent会调用相应的存储过程将数据更新到订阅端。这些存储过程分别是[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文件中。而第三条数据的日志也会被log reader传递到分发。 这样就会产生一个问题:如果仍然使用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