如果你有一个或多个在发布服务器上执行的存储过程并影响已发布的表,请考虑在发布中包含这些存储过程作为存储过程执行项目。 初始化订阅时,过程的定义(CREATE PROCEDURE 语句)将复制到订阅服务器;在发布服务器上执行该过程时,复制会在订阅服务器上执行相应的过程。 对于执行大型批处理操作的情况,这可以显著提升性能,因为只复制过程的执行,而无需复制每一行的具体更改。 例如,假设在发布数据库中创建以下存储过程:
CREATE PROC give_raise AS
UPDATE EMPLOYEES SET salary = salary * 1.10
此过程为公司中的 10,000 名员工提供 10% 的加薪。 在发布服务器上执行此存储过程时,它会更新每个员工的工资。 如果不复制存储过程的执行,更新将作为一个大型多步骤事务发送到订阅者:
BEGIN TRAN
UPDATE EMPLOYEES SET salary = salary * 1.10 WHERE PK = 'emp 1'
UPDATE EMPLOYEES SET salary = salary * 1.10 WHERE PK = 'emp 2'
这重复了 10,000 个更新。
复制存储过程执行时,复制仅发送命令以在订阅服务器上执行存储过程,而不是将所有更新写入分发数据库,然后将其通过网络发送到订阅服务器:
EXEC give_raise
重要
存储过程复制不适用于所有应用程序。 如果文章进行了水平筛选,这样在发布服务器上的行集与订阅服务器上的行集不同,那么两者执行相同的存储过程将返回不同的结果。 同样,如果更新基于另一个非重复表的子查询,在发布服务器和订阅服务器上执行相同的存储过程将返回不同的结果。
发布存储过程的执行
SQL Server Management Studio:在事务发布中发布执行存储过程(SQL Server Management Studio)
复制 Transact-SQL 编程:执行 sp_addarticle(Transact-SQL), 并为参数 @type指定“可序列化的 proc exec”或“proc exec”值。 有关定义项目的详细信息,请参阅 “定义项目”。
修改订阅服务器上的过程
默认情况下,发布服务器上的存储过程定义将传播到每个订阅服务器。 但是,还可以修改订阅服务器上的存储过程。 如果希望在发布服务器和订阅服务器上执行不同的逻辑,这非常有用。 例如,请考虑发布服务器上的存储过程 sp_big_delete,它具有两个功能:会从复制表big_table1删除 1,000,000 行并更新非复制表big_table2。 若要减少对网络资源的需求,应通过发布 sp_big_delete将 100 万行删除传播为存储过程。 在订阅服务器上,可以修改 sp_big_delete 以仅删除 100 万行,并且不对 big_table2执行后续更新。
注释
默认情况下,在发布服务器上使用 ALTER PROCEDURE 所做的任何更改将传播到订阅服务器。 若要防止这种情况,请在执行 ALTER PROCEDURE 之前禁用架构更改的传播。 有关架构更改的信息,请参阅 对发布数据库进行架构更改。
存储过程执行项目的类型
可通过两种不同的方式发布存储过程的执行:可序列化的过程执行项目和过程执行项目。
建议使用可序列化选项,因为它仅在过程在可序列化事务的上下文中执行时复制过程执行。 如果存储过程从可序列化事务外部执行,则对已发布表中数据的更改将作为一系列 DML 语句进行复制。 此行为有助于使订阅服务器上的数据与发布服务器上的数据保持一致。 这对于批量操作,特别是大规模清理操作,非常有用。
使用过程执行选项,有可能将执行复制到所有订阅服务器,而不管存储过程中的单个语句是否成功。 此外,由于存储过程对数据的更改可能发生在多个事务中,因此订阅服务器上的数据可能与发布服务器上的数据不一致。 若要解决这些问题,订阅者应为只读,并且必须使用比未提交读取更高的隔离级别。 如果使用未提交读取,则对已发布表中数据的更改将作为一系列 DML 语句进行复制。
以下示例说明了为何建议将过程复制设置为可序列化的过程对象。
BEGIN TRANSACTION T1
SELECT @var = max(col1) FROM tableA
UPDATE tableA SET col2 = <value>
WHERE col1 = @var
BEGIN TRANSACTION T2
INSERT tableA VALUES <values>
COMMIT TRANSACTION T2
在前面的示例中,假定事务 T1 中的 SELECT 发生在事务 T2 中的 INSERT 之前。
如果未在可序列化事务中执行该过程(隔离级别设置为 SERIALIZABLE),则允许事务 T2 在 T1 中的 SELECT 语句范围内插入一个新行,并将在 T1 之前提交。 这也意味着它将在 T1 之前应用于订阅者。 在订阅服务器上应用 T1 时,SELECT 可能会返回与发布服务器不同的值,并可能导致 UPDATE 的结果不同。
如果在可序列化事务中执行该过程,则不允许在 T2 中的 SELECT 语句涵盖的范围内插入事务 T2。 在 T1 提交以确保订阅者获得相同结果之前,会被阻止。
在可序列化事务中执行此过程时,锁将被保持更长时间,这可能导致并发性降低。
XACT_ABORT设置
复制存储过程执行时,执行存储过程的会话的设置应指定XACT_ABORT ON。 如果XACT_ABORT设置为 OFF,并且发布服务器上执行过程中发生错误,则订阅服务器上将发生相同的错误,从而导致分发代理失败。 通过指定 XACT_ABORT ON,可以确保在发布服务器上执行过程中遇到的任何错误都会导致整个执行过程回滚,从而避免分发代理失败。 有关设置XACT_ABORT的详细信息,请参阅 SET XACT_ABORT (Transact-SQL)。
如果需要设置 XACT_ABORT OFF,请为分发代理指定 -SkipErrors 参数。 这样,即使遇到错误,代理仍然可以继续在订阅方应用更改。