一個事務複製的bug--更新丟失

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

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

 

 

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

   

描述 設計

=== 日誌

環境:事務複製的訂閱是經過快照進行的初始化 server

條件:Logreader沒有運行。 blog

操做:咱們向publicaiton添加了一個新的Article. token

此時在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

相關文章
相關標籤/搜索