有兩種狀況會形成更新丟失,第一種是不正確的設置,例如外鍵或觸發器的「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。這個bug在sql server 2005,2008, 2008R2這些版本中均可以重現,而且目前沒有推出相應的fix. 須要注意的是,sql server 2012中相關的行爲已經從新進行了設計,不會存在這個問題。spa
描述 設計
=== 日誌
環境:事務複製的訂閱不是經過快照進行的初始化 server
條件:Logreader沒有運行。 blog
操做:咱們向publicaiton添加了一個新的Article. token
此時在log reader中止期間到完成添加article以前,publication的article出現了更新/刪除/插入,那麼這些變動都不會傳遞到訂閱。 假設 Logreader在11:00中止,咱們在12:00添加了一個新的artilce(假設瞬間完成),13:00從新啓動Logreader 。那麼11:00~12:00之間的更新將所有丟失。 進程
緣由 事務
===
當publicationdatabase的article發生更新時, 會產生相應的日誌,Log reader會讀取這些日誌信息,將他們寫入到Distribution 數據庫的msrepl_transactions和msrepl_commands中。具體的技術細節我會在之後的文章裏介紹。
Msrepl_transactions中的每一條記錄都有一個惟一標識xact_seqno,xact_seqno對應日誌中的LSN。 因此能夠經過xact_seqno推斷出他們在publicationdatabase中的生成順序,編號大的生成時間就晚,編號小的生成時間就早。
Distributionagent包含兩個進程,reader和writer。 Reader負責從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 = -1
)
這個存儲過程有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_transactions和msrepl_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
若是您暫時沒有辦法升級,能夠採用如下兩種方法:
更多(2013.10.23補充)
===
若是添加一個非快照初始化的訂閱時,該發佈對應的發佈數據庫的全部已存在訂閱也會出現更新丟失的狀況,不管這些都訂閱經過何種方式初始化,或屬於那個發佈.
由於添加訂閱的操做也會經過linked server向distribution database內寫入數據
解決方法和以前相同
該問題已經在sql server 2008 r2 sp2 cu13中解決(2014.07.01補充)