合併式複寫如何偵測並解決衝突

合併式複寫可讓多個節點進行自發的資料變更,因此會出現在一個節點上所作的變更與其他節點上對相同資料所作變更發生衝突的情況。在另一些情況下,「合併代理程式」會遇到如條件約束違規和無法將特定節點處所作的變更傳播到其他節點的錯誤。本主題描述了衝突類型、偵測並解決衝突的方式,以及影響衝突偵測和解決的因素。

偵測並解決衝突

「合併代理程式」使用 MSmerge_contents 系統資料表的 lineage 資料行偵測衝突;如果啟用發行項的資料行層級追蹤,則也會使用 COLV1 資料行。這些資料行包含了一些中繼資料,分別是有關何時插入資料列或資料行,以及合併式複寫拓撲中的哪些節點對該資料列或資料行作了變更。您可以使用系統預存程序 sp_showrowreplicainfo (Transact-SQL) 來檢視這些中繼資料。

由於「合併代理程式」會列舉同步處理期間要套用的變更,因此它會比較發行者端和訂閱者端每一個資料列的中繼資料。「合併代理程式」根據此中繼資料來判斷資料列或資料行是否已在拓撲的多個節點上發生了變更,這表示可能會發生衝突。偵測到衝突後,「合併代理程式」會啟動指定給有衝突之發行項的衝突解析程式,並使用該解析程式來決定衝突成功者。成功的資料列已套用至「發行者」與「訂閱者」,而失敗的資料列則已寫入衝突資料表。

除非您已為發行項選擇了互動衝突解決方案,否則「合併代理程式」會立即自動解決衝突。如需詳細資訊,請參閱<互動式衝突解決>。如果使用合併式複寫「衝突檢視器」手動變更衝突的優先資料列,則「合併代理程式」會在下次同步處理期間將資料列的優先版本套用至失敗的伺服器。

記錄解決的衝突

「合併代理程式」根據衝突解析程式中的邏輯解決衝突後,會根據衝突的類型記錄衝突資料:

  • 對於「更新」和「插入」衝突,它會將資料列的失敗版本寫入該發行項的衝突資料表,名稱格式為 conflict_<PublicationName>_<ArticleName>。一般衝突資訊 (如衝突類型) 將寫入資料表 MSmerge_conflicts_info

  • 對於「刪除」衝突,它會將資料列的失敗版本寫入 MSmerge_conflicts_info 資料表。如果刪除優先於更新,則不存在失敗資料列的資料 (因為它被刪除),因此不會有任何資料寫入 conflict_<PublicationName>_<ArticleName>

每個發行項的衝突資料表將在發行集資料庫、訂閱資料庫或兩者 (預設值) 中建立,視為 sp_addmergepublication@conflict_logging 參數指定的值而定。每個衝突資料表與其所依據之發行項的結構相同,但增加了 origin_datasource_id 資料行。如果衝突資料表中的資料超出了發行集衝突保留期限,「合併代理程式」便會刪除資料。衝突保留期限可使用 sp_addmergepublication@conflict_retention 參數指定 (預設值是 14 天)。

複寫提供「複寫衝突檢視器」和預存程序 (sp_helpmergearticleconflictssp_helpmergeconflictrowssp_helpmergedeleteconflictrows) 以檢視衝突資料。如需詳細資訊,請參閱<如何:檢視並解決合併式發行集的資料衝突 (SQL Server Management Studio)>和<如何:檢視合併式發行集的衝突資訊 (複寫 Transact-SQL 程式設計)>。

影響衝突解決方案的因素

有兩個因素會影響「合併代理程式」解決所偵測到之衝突的方式:

  • 訂閱類型:客訂閱或主訂閱 (訂閱為提取訂閱或發送訂閱不影響衝突解決方案)。

  • 使用的衝突追蹤類型:資料列層級、資料行層級或邏輯記錄層級。

訂閱類型

在建立訂閱時,除指定是提取訂閱或發送訂閱以外,您還要指定它是客訂閱還是主訂閱;建立訂閱後,將無法變更其類型 (在舊版的 Microsoft SQL Server 中,客訂閱與主訂閱分別指本機和全域訂閱)。

指派了優先權值 (從 0.00 到 99.99) 的訂閱稱為主訂閱;使用「發行者」優先權值的訂閱稱為客訂閱。此外,具有主訂閱的「訂閱者」可以將資料重新發行至其他「訂閱者」。下表摘要說明兩種「訂閱者」類型的主要差異和用途。

類型

優先權值

已使用

伺服器

使用者指派

希望不同的「訂閱者」具有不同的優先權。

用戶端

0.00,但資料變更會在同步處理之後取得「發行者」的優先權值

希望所有「訂閱者」具有相同的優先權,且第一個「訂閱者」與「發行者」合併,以在衝突中獲勝。

如果資料列在客訂閱中發生變更,則在同步處理訂閱之前,不會為變更指派優先權。在同步處理期間,「發行者」的優先權會指派到來自「訂閱者」的變更,且變更會為後續的同步處理保留該優先權。從某種意義上說,「發行者」便取得了變更的擁有權。此行為允許第一個「訂閱者」與「發行者」同步,以便之後在給定資料列或資料行上與其他「訂閱者」發生衝突時獲勝。

當您在主訂閱中變更資料列時,訂閱優先權會儲存至變更的中繼資料。當此變更資料列與其他「訂閱者」的變更合併時,這個優先權值會隨著它四處旅行。這可確保較高優先權訂閱所進行的變更不會被較低優先權訂閱的後續變更所取代。

訂閱不能擁有高於其「發行者」的明確優先權值。合併式複寫拓撲中的最上層「發行者」始終擁有明確優先權值 100.00。該發行集所有訂閱的優先權值必須低於此值。在重新發行拓撲中:

  • 如果「訂閱者」要重新發行資料,則訂閱必須為優先權值低於「發行者」而高於「訂閱者」的主訂閱。

  • 如果「訂閱者」不重新發行資料 (因為它處於重新發行樹狀結構的分葉層級),則訂閱必須為客訂閱。

如需有關伺服器訂閱和優先權的詳細資訊,請參閱<以訂閱類型和指派的優先權為基礎之合併衝突解決範例>。

延遲的衝突通知

如果主訂閱擁有不同的衝突優先權,則會出現延遲的衝突通知。請考慮下列狀況,即對高優先權「訂閱者」與「發行者」進行同步處理時,在導致衝突變更的「發行者」與低優先權「訂閱者」之間交換非衝突變更:

  1. 「發行者」與低優先權「訂閱者」(命名為 LowPrioritySub) 透過一些同步處理來交換變更,不會發生衝突。

  2. 高優先權「訂閱者」(命名為 HighPrioritySub) 有時並未與「發行者」同步,且對 LowPrioritySub「訂閱者」已作變更的資料列進行了變更。

  3. HighPrioritySub「訂閱者」與「發行者」同步,並在與 LowPrioritySub「訂閱者」之間的變更衝突中獲勝,因為它比 LowPrioritySub「訂閱者」擁有更高的優先權。「發行者」目前包含由 HighPrioritySub「訂閱者」所作的變更。

  4. 隨後,LowPrioritySub「訂閱者」可與「發行者」合併,並下載大量因與 HighPrioritySub「訂閱者」衝突而導致的變更。

當低優先權「訂閱者」對目前為衝突失敗者的相同資料列作變更時,此情況將出現問題。這會導致遺失由此「訂閱者」所作的全部變更。解決此問題的一個可能方案是確定所有「訂閱者」擁有相同的優先權,除非商務邏輯另有規定。

追蹤層級

資料變更是否算是衝突取決於您為發行項所設定的衝突追蹤類型:資料列層級、資料行層級或邏輯記錄層級。如需邏輯記錄層級追蹤的詳細資訊,請參閱<偵測和解決邏輯記錄中的衝突>。

當衝突在資料列層級辨識時,在對應資料列上所作的變更會被判斷為衝突,無論這些變更是否發生在相同資料行上。例如,假設「發行者」資料列的地址資料行作了變更,而第二個變更發生在對應「訂閱者」資料列的電話號碼資料行 (在相同資料表中)。若使用資料列層級追蹤,便會偵測到衝突,因為對相同資料列作了變更。若使用資料行層級追蹤,則不會偵測到衝突,因為變更發生在相同資料列的不同資料行上。

對於資料列層級和資料行層級追蹤,衝突的解決方案相同:整列資料會被衝突中獲勝的資料所覆寫 (對於邏輯記錄層級追蹤,解決方案依發行項屬性 logical_record_level_conflict_resolution 而定)。

應用程式語意通常決定了要使用何種追蹤選項。例如,當您更新一般均在同一時間輸入的客戶資料 (如地址或電話號碼) 時,應該選用資料列層級追蹤。如果是在這種狀況下選擇資料行層級追蹤,則在某處變更客戶地址並在另一處變更客戶電話號碼並不會被偵測為衝突:可能會在同步處理時合併資料且錯誤可能被遺漏。在其他情況下,從不同站台上更新個別資料行可能是最合邏輯的選擇。例如,可能有兩個站台存取了某個客戶不同類型的統計資訊,例如收入等級和信用卡購物總金額。此時選取資料行層級追蹤可確保兩個站台均能進入不同資料行中的統計資料,不會產生不必要的衝突。

[!附註]

如果您的應用程式不需要資料行層級追蹤,建議您使用資料列層級追蹤 (預設值),因為其同步處理效能通常更高。如果使用資料列追蹤,則基底資料表可包括的資料行行數上限為 1,024,不過,因為必須從發行項篩選資料行,所以發行的資料行行數上限為 246。如果使用資料行追蹤,則基底資料表可包括的資料行數上限為 246。

衝突類型

雖然大多數衝突與更新有關 (某節點上所作的更新與另一節點上所作的更新或刪除有衝突),但也存在其他類型的衝突。本節中所討論的各類衝突,均有可能在合併處理的上載或下載階段出現。上載處理是在特定合併階段執行的第一個變更重新調整,並且是「合併代理程式」將變更從「訂閱者」複寫到「發行者」的階段。在此處理過程中偵測到的衝突稱為上載衝突。下載處理涉及將變更從「發行者」移至「訂閱者」,並在上載處理後發生。在此處理階段中發生的衝突稱為下載衝突。

如需有關衝突類型的詳細資訊,請參閱<MSmerge_conflicts_info (Transact-SQL)>,尤其是 conflict_typereason_code 資料行。

更新與更新衝突

當在一個節點上對資料列 (或資料行、邏輯記錄) 所作的更新與在另一個節點上對相同資料列所作的更新發生衝突時,「合併代理程式」會偵測到更新與更新衝突。在此情況下,預設解析程式的行為是將資料列的獲勝版本傳送至失敗節點,並在發行項衝突資料表中記錄失敗的資料列版本。

更新與刪除衝突

當在一個節點上對資料的更新與另一個節點上對資料的刪除發生衝突時,「合併代理程式」會偵測到更新與刪除衝突。在此情況下,「合併代理程式」會更新資料列;但是,當「合併代理程式」在目的地搜尋該資料列時將找不到資料列,因為資料列已刪除。如果獲勝者是更新資料列的節點,則將捨棄在失敗節點上所作的刪除,且「合併代理程式」會將最新更新的資料列傳送給衝突失敗者。「合併代理程式」會將資料列的失敗版本資訊記錄到 MSmerge_conflicts_info 資料表。

無法變更衝突

「合併代理程式」會在無法套用特定變更時引發此類衝突。這通常是由「發行者」與「訂閱者」之間的條件約束定義不同,以及在條件約束中使用了 NOT FOR REPLICATION (NFR) 屬性而引起的。範例如下:

  • 外部索引鍵在訂閱者端有衝突,可能發生於訂閱者端條件約束未標示為 NFR 的情況。

  • 「發行者」與「訂閱者」之間的條件約束不同,並且條件約束未標示為 NFR。

  • 訂閱者端無法使用相依物件。例如,如果您發行一個檢視,但未發行檢視相依的資料表,則當您嘗試在訂閱者端透過該檢視進行插入時會出現失敗。

  • 與主索引鍵和外部索引鍵條件約束不符之發行集的聯結篩選邏輯。當 SQL Server 關聯式引擎嘗試接受條件約束,但「合併代理程式」正在接受發行項之間的聯結篩選定義時,會發生衝突。「合併代理程式」無法套用目的地節點端的變更,因為資料表層級條件約束會導致衝突。

  • 如果為發行項定義了識別欄位,並且未使用自動識別管理,則會發生衝突,因為會出現唯一索引或唯一條件約束違規或主索引鍵違規。這種情況在兩個「訂閱者」對新插入的資料列使用相同的識別值時,將會出現問題。如需識別範圍管理的詳細資訊,請參閱<複寫識別欄位>。

  • 由觸發程序邏輯引起的衝突將阻止「合併代理程式」將資料列插入目的地資料表。請考慮在訂閱者端定義更新觸發程序;觸發程序不標示為 NFR,並在其邏輯中包含「回復」。如果發生失敗,觸發程序將發出交易「回復」,這將使「合併代理程式」偵測到無法變更衝突。