一個事務復制的bug--更新丟失


有兩種情況會造成更新丟失,第一種是不正確的設置,例如外鍵或觸發器的“Not For Replication” (NFR)屬性沒有開啟。詳情請參考http://blogs.msdn.com/b/apgcdsd/archive/2012/01/10/10254809.aspx

第二種是產品bug,例如使用了 MaxCmdsInTran http://support.microsoft.com/kb/2648158

 

 

前一陣我在做case的時候遇到了一個新的bug。這個bugsql server 2005,2008,  2008R2這些版本中都可以重現,並且目前沒有推出相應的fix.  需要注意的是,sql server 2012中相關的行為已經重新進行了設計,不會存在這個問題。

   

描述

===

環境:事務復制的訂閱是通過快照進行的初始化

條件:Logreader沒有運行。

操作:我們向publicaiton添加了一個新的Article.

此時在log reader停止期間到完成添加article之前,publicationarticle出現了更新/刪除/插入,那么這些變更都不會傳遞到訂閱。 假設 Logreader在11:00停止,我們在12:00添加了一個新的artilce(假設瞬間完成),13:00重新啟動Logreader 。那么11:00~12:00之間的更新將全部丟失。

 

原因

===

publicationdatabasearticle發生更新時, 會產生相應的日志,Log reader會讀取這些日志信息,將他們寫入到Distribution 數據庫的msrepl_transactionsmsrepl_commands中。具體的技術細節我會在以后的文章里介紹。

 

Msrepl_transactions中的每一條記錄都有一個唯一標識xact_seqnoxact_seqno對應日志中的LSN。 所以可以通過xact_seqno推斷出他們在publicationdatabase中的生成順序,編號大的生成時間就晚,編號小的生成時間就早。

Distributionagent包含兩個進程,readerwriterReader負責從Distribution 數據庫中讀取數據,Writer負責將reader讀取的數據寫入到訂閱數據庫.

reader是通過sp_MSget_repl_commands來讀取Distribution數據庫中(讀取Msrepl_transactions表和Msrepl_Commands表)的數據

下面是sp_MSget_repl_commands的參數定義

CREATE PROCEDURE sys.sp_MSget_repl_commands 

( 

@agent_id int, 

@last_xact_seqno varbinary(16), 

@get_count tinyint = 0,  -- 0 = no count, 1 = cmd and tran (legacy), 2 = cmd only 

@compatibility_level int = 7000000, 

@subdb_version int = 0, 

@read_query_size int = -

) 

這個存儲過程有6個參數,在Transactionalreplication 中,只會使用前4個(並且第三個參數和第四個參數的值是固定不變的.分別為0和10000000)。下面是一個例子:

execsp_MSget_repl_commands 46,0x0010630F000002A900EA00000000,0,10000000

@agent_id表示Distributionagentid,每個訂閱都會有一個單獨的Distributionagent來處理數據。 帶入@agent_id后,就可以找到訂閱對應的publication 和所有的article

@last_xact_seqno 表示上一次傳遞到訂閱的LSN

大致邏輯是:Reader讀取分發數據庫中LSN大於@last_xact_seqno的數據。 Writer將讀取到的數據寫入訂閱,並更新相應的LSN.(subscription數據庫的 MSreplication_subscriptions表的 transaction_timestamp列和Distribution數據庫的msDistribution_history表的xact_seqno列)。然后Reader會繼續用新的LSN來讀取后續的數據,再傳遞給Writer,如此往復。

 

假設現在訂閱段的數據已經更新到了0x0010630F000002A900EA00000000 之后我們認為地向Msrepl_transactions表和Msrepl_Commands表插入了一批數據,xact_seqno對為0x0010630E000002A900EA00000000. 雖然這些數據的格式全部有效, 但Distributionagent是不會讀取這些新加入的數據的,因為他們"太舊了"(他們的xact_seqno小於訂閱的xact_senqo).

 

Log reader停止時, 我們是可以添加article的。 並且相應的操作也會向msrepl_transactionsmsrepl_commands插入數據,這些數據並不是由Log reader傳遞的,而是通過linkedserver直接向Distributor直接寫入數據。 Distributionagent會讀取這些數據,並更新相應的xact_seqno 而"Log reader停止"到"添加新article"這段期間發布產生的數據的xact_seqno是小於"添加新article"的xact_seqno,所以這些跟新會丟失。 說起來比較抽象,下面舉個例子。

 

10:00到11:00期間publication database共生成了三條事務,對應的xact_seqno分別為

0x0010630F000006B9001E

0x0010630F000006F10004

0x0010630F000006F20004

 

11:01將log reader停止

 

11:01~12:00期間publication database共生成了4條事務

0x0010630F000006F30004

0x0010630F000007080005

0x0010630F000007D40205

0x0010630F0000098C005C

 

但由於log reader沒有啟動,所以msrepl_transactions表內依然是三條數據. 12:01完成添加article的操作,msrepl_transactions內生成相應的記錄0x00106310000000100100。

此時msrepl_transations內共有四條記錄

此時訂閱端的xact_seqno為0x0010630F000006F20004, distribution agent將讀取大於0x0010630F000006F20004的數據, 只有0x00106310000000100100符合要求。 訂閱段完成的所有的數據同后,相應的xact_seqno為0x00106310000000100100

 

 

此時開啟log reader, 累計的數據傳遞完畢后,msrepl_transactions共有8條記錄,但distribution agent不會再去讀取之前的數據了…

 

影響

===

在有些極端情況下,即使您認為Log reader處於運行狀態,還是會出現跟新丟失的情況。原因在有些情況下,Log reader成為了deadlock的犧牲者被kill掉,但Log readeragentjob有自動retry的機制,會在一段時間后自動恢復, 所以您可能無法察覺。 是否出現死鎖,您查詢MSLog reader_history,MSrepl_errors會找到類似的"N'Transaction (Process ID 400) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.',NULL,1,N'  SQL Log Reader Agent encountered an error.    Publisher: xxx Publisher Database: xxx".

 

 

解決辦法

===

Distributor升級至sql servr 2012

如果您暫時沒有辦法升級,可以采用以下兩種方法:

  1. 添加一個新的發布,將新的article添加到發布中。
  2. 在添加article前將Log readerDistributionagent停止。添加完成后,啟動Log reader,確認Log reader已經將之前的數據傳送到了Distribution數據庫后(可以使用tracertoken來確認Log reader是否已經完成同步), 啟動Distributionagent。這樣就可以避免數據丟失了。

更多(2013.10.23補充)

===

如果添加一個非快照初始化的訂閱時,該發布對應的發布數據庫的所有已存在訂閱也會出現更新丟失的情況,無論這些都訂閱通過何種方式初始化,或屬於那個發布.

因為添加訂閱的操作也會通過linked server向distribution database內寫入數據

解決方法和之前相同

 

該問題已經在sql server 2008 r2 sp2 cu13中解決(2014.07.01補充)

http://support.microsoft.com/kb/2922526

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM