在交易式複寫中發佈預存程序執行

適用於:SQL ServerAzure SQL 受控執行個體

如果您有一個或多個在發行者端執行且會影響已發行資料表的預存程序,請考慮將這些預存程序作為預存程序執行文章納入您的發行集。 預存程序的定義(即 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  

重要

預存程序複寫並不適用於所有應用程式。 如果發行項經水平篩選,以使「發行者」與「訂閱者」端的資料列組不同,則在兩端執行相同的預存程序將傳回不同的結果。 同樣地,如果更新是基於其他非複寫資料表的子查詢,則在「發行者」和「訂閱者」端執行相同的預存程序也將傳回不同的結果。

若要發佈預存程序的執行作業

在訂閱者端修改程序

根據預設值,「發行者」的預存程序定義會傳播到各個「訂閱者」。 但是,您也可以在「訂閱者」端修改預存程序。 若您想在「發行者」和「訂閱者」執行不同的邏輯,這個方式就非常有幫助。 例如,考慮 sp_big_delete這個「發行者」的預存程序有兩個功能:它會從複寫資料表 big_table1 刪除 1,000,000 個資料列,然後更新非複寫的資料表 big_table2。 若要降低網路資源的需求,您應該透過發行 sp_big_delete,將一百萬個資料列刪除做為預存程序傳播。 在「訂閱者」端,您可以將 sp_big_delete 修改為只刪除一百萬個資料列,並且不執行後續對 big_table2的更新。

注意

預設情況下,使用 ALTER PROCEDURE Publisher 所做的任何變更都會傳遞給訂閱者。 為避免此情況,執行前先停用 ALTER PROCEDURE結構變更的傳播。 如需結構描述變更的資訊,請參閱對發行集資料庫進行結構描述變更。

預存程序執行發行項的類型

可以透過兩種不同方式發行預存程序的執行:序列化程序執行發行項與程序執行發行項。

  • 建議使用可序列化選項,因為只有在程序於可序列化交易中執行時,才會複寫該程序的執行。 若預存程序從序列化交易外部執行時,對已發行的資料表中所做的資料變更,會複寫為一連串的 DML 陳述式。 這個行為保證「訂閱者」的資料可以和「發行者」的資料一致。 這個方法對於批次作業而言特別有用,例如大型的清除作業。

  • 使用程序執行選項時,可能會將執行作業複寫到所有訂閱者,而不論預存程序中的個別陳述式是否成功。 此外,因為預存程序對資料所做的變更,可以發生在多個交易中,「訂閱者」的資料可能不會與「發行者」的資料一致。 若要解決這些問題,訂閱者必須為唯讀,且您使用的隔離層級必須高於讀取未認可。 如果您使用 read uncommitted,則已發佈資料表中的資料變更會以一連串 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 發生在 INSERT 交易 T2 之前。

如果該程序不是在可序列化交易中執行(隔離等級設為 SERIALIZABLE),則交易 T2 將可在 T1 的 SELECT 敘述所涵蓋的範圍內插入新的資料列,並且會在 T1 之前提交。 這也表示,它會在 T1 之前套用於訂閱者。 當在訂閱者端套用 T1 時,SELECT 可能會回傳與發行者端不同的值,並可能使 UPDATE 的結果有所不同。

如果程序執行於序列化交易內,交易 T2 則不被允許在 T2 的 SELECT 陳述式所涵蓋的範圍內插入。 在 T1 提交之前,此動作將遭到封鎖,以確保訂閱者端得到相同的結果。

當您在可序列化交易中執行此程序時,鎖定會維持更久,並可能導致並行性降低。

XACT_ABORT 設定

在複製儲存程序執行時,執行該儲存程序的會話設定應指定 XACT_ABORT 為 ON。 若 XACT_ABORT 設為關閉,且在 Publisher 執行程序時發生錯誤,訂閱者也會發生同樣錯誤,導致 散發代理程式 失敗。 將 XACT_ABORT 指定為 ON,可確保在 Publisher 端執行期間遇到的任何錯誤,都會導致整個執行作業回滾,從而避免 散發代理程式 發生失敗。 關於設定 XACT_ABORT的更多資訊,請參見 SET XACT_ABORT (Transact-SQL)。

如果你需要設定為 XACT_ABORT OFF,請為 散發代理程式 指定 -SkipErrors 參數。 這將允許代理程式在遇到錯誤後,繼續在「訂閱者」端套用變更。