最近幫一個客戶搭建跨洋的合併複製,由於資料庫非常大,跨洋網路條件不穩定,因此只能通過備份初始化,在初始化完成後向海外訂閱端插入資料時發現報出如下錯誤:
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所示。
圖1.合併複製所加的約束
我們看到該約束的定義只允許4002到5002以及5002到6002之間的資料被插入。
此時如果出現了一些BUG或者人為改動了該表自增列種子的值,則會報錯,如圖2所示。
圖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
圖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列,但這涉及到表結構以及程式的修改,而且需要重新初始化複製,因此不是每一個環境都有條件這麼做。
此時,就可以正常插入資料了。