SQL Server 合併複製遇到identity range check報錯的解決

CareySon發表於2015-09-02

    最近幫一個客戶搭建跨洋的合併複製,由於資料庫非常大,跨洋網路條件不穩定,因此只能通過備份初始化,在初始化完成後向海外訂閱端插入資料時發現報出如下錯誤:

Msg 548, Level 16, State 2, Line 2
The insert failed. It conflicted with an identity range check constraint in database %s, replicated table %s, column %s. If the identity column is automatically managed by replication, update the range as follows: for the Publisher, execute sp_adjustpublisheridentityrange; for the Subscriber, run the Distribution Agent or the Merge Agent.

 

原因?

    在SQL Server中,對於自增列的定義是對於每一條新插入的行,都會自動按照順序新生成一個遞增的數字,改數字通常和業務無關且被用於作為主鍵。但如果該表用於可更新事務複製或者合併複製,那麼該自增列的區間範圍則由複製管理。

    此時,複製可以保證自增列可控,因為複製代理插入行時不會導致自增列自增,只有使用者顯式插入時才會導致自增列自增。

    讓我們來做一個實驗。首先建立表,表定義如下:

CREATE TABLE [dbo].[Table_1](
    [c1] [int] IDENTITY(1,1),
    [c2] [int] NULL,
    [ROWGUID] [uniqueidentifier] NOT NULL,
    [rowguid4] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
 CONSTRAINT [PK_Table_1] PRIMARY KEY CLUSTERED 
(
    [c1] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

 

    此時我們對建立合併複製,並把該表包含在內,並使用快照代理初始化複製,當完成該步驟時,我們發現該表上自動多了兩個約束,如圖1所示。

image

圖1.合併複製所加的約束

    

     我們看到該約束的定義只允許4002到5002以及5002到6002之間的資料被插入。

     此時如果出現了一些BUG或者人為改動了該表自增列種子的值,則會報錯,如圖2所示。

image

圖2.改動種子值導致插入資料出錯

 

    該約束會由合併代理自動遞增,比如說我們用如下程式碼插入2000條資料,則發現該約束會自動遞增如圖3所示。

DECLARE @index INT=1
WHILE @index<2000
BEGIN
INSERT INTO table_1(c2,ROWGUID) VALUES(2,NEWID())
SET @index=@index+1
END

  

image

   圖3.約束區間自動遞增滑動

 

解決辦法  

    此時我已經找出了上面報錯的原因,因為是由於從備份初始化,那麼備份以及備份傳輸期間釋出庫又有新的資料插入,此時釋出庫比如說,該表的種子大小已經增加到了6000,而備份中該表大小還是5000,而約束已經滑動到了6000,那麼在訂閱端插入資料時就會發生這種問題。

解決辦法1

    在釋出端使用sp_adjustpublisheridentityrange 儲存過程使得約束範圍自動向後滑動,比如從6000-8000滑動到8000-10000。缺點自增值之間會有一個GAP。如果業務允許,推薦使用該做法。

    sp_adjustpublisheridentityrange @table_name=’表名稱‘

解決辦法2

    在釋出端執行SELECT  IDENT_CURRENT('表名稱'),找到釋出表的種子值。在訂閱端通過DBCC CHECKIDENT (表名稱,RESEED, 設定為上面值)命令將兩端種子值設定為一致。

解決辦法3

    在訂閱端執行合併代理,即可修復資料。如果此方法不行,則再次嘗試上述方法。

解決辦法4

    不用自增列,而使用GUID列,但這涉及到表結構以及程式的修改,而且需要重新初始化複製,因此不是每一個環境都有條件這麼做。

   

此時,就可以正常插入資料了。

相關文章