在 SQL Server 交易式複寫環境中,資料同步延遲是常見挑戰。當發行端執行大量資料更新(如 UPDATE ta SET col = ? WHERE ?)時,該交易會在訂閱端拆解為逐行命令應用,導致散發代理程式長時間佔用資源,嚴重拖慢後續同步。若無法等待其完成,必須採取干預措施跳過該交易,讓複寫恢復正常流程。
發行資料庫的文章更新會產生交易記錄,記錄讀取器將其寫入散發資料庫的 msrepl_transactions 與 msrepl_commands 表。每筆交易擁有唯一 xact_seqno,對應發行端的 LSN(日誌序號),序號越大代表生成時間越晚。
散發代理包含 Reader 與 Writer 兩個子執行緒:
sp_MSget_repl_commands 從散發資料庫讀取命令。關鍵參數 @last_xact_seqno 代表上次成功傳遞至訂閱端的 LSN。Reader 從訂閱端 MSreplication_subscriptions 表的 transaction_timestamp 取得該值,並讀取大於此 LSN 的資料;Writer 寫入後更新該欄位,形成循環。
環境架構

問題重現:11:00 執行 UPDATE ta SET c = -11,隨後陸續對 ta、tb、tc 插入資料。啟動複寫監視器發現嚴重延遲,散發代理持續傳遞大量更新命令。

DECLARE @publisher sysname, @publicationDB sysname, @publication sysname; SET @publisher = 'SQL108W2K8R22'; SET @publicationDB = 'pubdb'; SET @publication = 'pubdbtest2'; SELECT transaction_timestamp FROM MSreplication_subscriptions WHERE publisher = @publisher AND publisher_db = @publicationDB AND publication = @publication;
假設取得 0x0000014900004E9A0004000000000000。
DECLARE @publisher sysname, @publicationDB sysname, @publication sysname, @transaction_timestamp varbinary(16);
SET @publisher = 'SQL108W2K8R21';
SET @publicationDB = 'publicationdb2';
SET @publication = 'pubtest';
SET @transaction_timestamp = 0x0000014900004E9A0004000000000000;
SELECT TOP 1 xact_seqno
FROM MSrepl_commands WITH (NOLOCK)
WHERE xact_seqno > @transaction_timestamp
AND article_id IN (
SELECT a.article_id
FROM MSarticles a
INNER JOIN MSpublications p ON a.publication_id = p.publication_id
AND a.publisher_id = p.publisher_id
AND a.publisher_db = p.publisher_db
INNER JOIN sys.servers s ON s.server_id = p.publisher_id
WHERE p.publication = @publication
AND p.publisher_db = @publicationDB
AND s.name = @publisher
)
AND publisher_database_id = (
SELECT pd.id
FROM MSpublisher_databases pd
INNER JOIN MSpublications p ON pd.publisher_id = p.publisher_id
INNER JOIN sys.servers s ON pd.publisher_id = s.server_id
AND pd.publisher_db = p.publisher_db
WHERE s.name = @publisher
AND p.publication = @publication
AND pd.publisher_db = @publicationDB
)
ORDER BY xact_seqno;
假設取得 0x0000018C000001000171,即為要跳過至的目標 LSN。
DECLARE @publisher sysname, @publicationDB sysname, @publication sysname, @transaction_timestamp varbinary(16); SET @publisher = 'SQL108W2K8R22'; SET @publicationDB = 'pubdb'; SET @publication = 'pubdbtest2'; SET @transaction_timestamp = 0x0000018C000001000171; UPDATE MSreplication_subscriptions SET transaction_timestamp = @transaction_timestamp WHERE publisher = @publisher AND publisher_db = @publicationDB AND publication = @publication;
執行完成後重新啟動散發代理作業,即可發現大型交易已被跳過,ta 表在訂閱端不會套用該更新,後續交易陸續同步,延遲消失。
注意:此操作會導致訂閱端資料與發行端暫時不一致,需事後透過資料比對或重新初始化修復。僅建議在無法容忍延遲且確認該交易可接受遺失時使用。