最近帮一个客户搭建跨洋的合并复制,由于数据库非常大,跨洋网络条件不稳定,因此只能通过备份初始化,在初始化完成后向海外订阅端插入数据时发现报出如下错误:

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,那么在订阅端插入数据时就会发生这种问题(意思就是说订阅端的备份表种子还在5000,但是备份表的约束已经到了6000,这时在订阅端的备份表中插入一条5000多的值肯定会报错,因为违反了约束)。

再举一个例子,如本文前面所述,实际上合并复制中订阅端的自增列是由复制管理来控制的,复制代理在订阅端插入行时,自增列都是设置"SET IDENTITY_INSERT [表名] ON"后,通过指定值来插入的。而实际上订阅端表自增列的种子会是一个比较大的值,例如下面这个Person表是订阅端的一张表(ID列是自增列),我们可以看到其内部只有5条数据,是通过合并复制从发布端同步过来的:

但是我们可以看到其自增列ID的种子已经高达2006:

这就是因为Replication的复制管理将订阅端Person表的种子设置为了2006,所有由用户显式插入订阅端Person表的数据,其ID列都会从2006开始。

这时我们在发布端的Person表中插入10000多行数据,然后启动复制代理将其同步到订阅端的Person表,可以看到订阅端的Person表ID已经增长到35103:

但是合并复制同步发布端和订阅端数据后,不会更新订阅端Person表的种子值,导致订阅端Person表的种子值还是2006:

最关键的问题是订阅端Person表的约束也没有更新,还是在10000以下的(合并复制会给表的自增列预留一段ID值,所以10000以下的ID值在发布端和订阅端的Person表中都是没有使用的)。

那么这时用户显式插入大量数据(假如100000行)到订阅端Person表中,其ID列还是会从2006开始,会导致违反订阅端Person表的约束(因为约束值在10000以下,100000行数据会使ID列自增超过约束值),所以就会产生本文最上面所述的错误。

但是在合并复制的发布端没有这个问题,因为发布端Person表的约束值会随着数据的插入自动向后滑动,但是订阅端Person表的约束值就不会滑动,除非和发布端进行一次同步。

此外如果取消合并复制订阅端数据库的订阅,订阅端数据库中所有合并复制的约束会被删除,所有表中合并复制创建的RowGuid列也会被删除,并且Person表的种子值会被设置为当前ID列的最大值35103,这时随便怎么往订阅端数据库的Person表中插入数据都是没有问题的了。

解决办法1
在发布端使用sp_adjustpublisheridentityrange 存储过程使得约束范围自动向后滑动,比如从6000-8000滑动到8000-10000。缺点自增值之间会有一个GAP。如果业务允许,推荐使用该做法。

sp_adjustpublisheridentityrange @table_name='表名称'

解决办法2
在发布端运行SELECT  IDENT_CURRENT('表名称'),找到发布表的种子值。在订阅端通过DBCC CHECKIDENT (表名称,RESEED, 设置为上面值)命令将两端种子值设置为一致。

解决办法3
在订阅端运行合并代理,即可修复数据。如果此方法不行,则再次尝试上述方法。

解决办法4
不用自增列,而使用GUID列,但这涉及到表结构以及程序的修改,而且需要重新初始化复制,因此不是每一个环境都有条件这么做。

这里个人建议在Replication的合并复制中,如果数据库表中有自增列(IDENTITY),这种情况下,最好不要让用户显式更改订阅端的数据库数据,所有的数据更新都来自发布端是最安全的。

另外顺便补充下,如果在Replication的合并复制中,发布端数据库新建了表或其它对象,将其添加到合并复制的发布后,需要重新在发布端生成新的快照后,才能将新加的表或对象同步到订阅端数据库中。

原文链接

SQL Server 合并复制遇到identity range check报错的解决 (转载)的更多相关文章

  1. SQL Server 合并复制遇到identity range check报错的解决

        最近帮一个客户搭建跨洋的合并复制,由于数据库非常大,跨洋网络条件不稳定,因此只能通过备份初始化,在初始化完成后向海外订阅端插入数据时发现报出如下错误: Msg 548, Level 16, S ...

  2. Sql Server 2008卸载后再次安装一直报错

    sql server 2008卸载之后再次安装一直报错问题. 第一:由于上一次的卸载不干净,可参照百度完全卸载sql server2008 的方式 1. 用WindowsInstaller删除所有与S ...

  3. SQL server 维护计划中 “清除维护任务” 执行报错

    SQL server 维护计划中 “清除维护任务” 执行报错,错误如下: 执行查询“EXECUTE master.dbo.xp_delete_file 0,N'',N'',N'2019...”失败,错 ...

  4. SQL Server 合并复制的Article可以指定单个对象的更新方向

    如下所示,这是SQL Server中一个合并复制发布端的Article: 我们可以在Article中选择一个对象,比如这里我们选择MD.Car表,点击鼠标右键,选择"Set Properti ...

  5. SQL Server 合并复制如何把备份的发布端或订阅端BAK文件还原为数据库

    SQL Server的合并复制,是可以备份发布端和订阅端数据库为BAK文件的,但是问题是合并复制在数据库中自动创建的系统表.触发器.表中的RowGuid列等也会被一起备份. 这里我们举个例子,下面图中 ...

  6. 关于SQL Server 各种安装失败均失败,报错“等待数据库引擎恢复句柄失败”的经验分享

    最近安装SQL 2019遇到这个问题,试过自己合网上几乎所有办法,怎么都安装不上,最后在微软社区解决了,由于这个问题比较特殊,并且网上几乎没有正确的决绝方案,因此将我的解决过程及经验记录分享一下,也为 ...

  7. Ubuntu Server 上安装pip后pip命令报错的解决办法

    Installation Do I need to install pip? pip is already installed if you are using Python 2 >=2.7.9 ...

  8. SQL Server2008数据库报错与解决方法

    一. 报错信息 启动MSSQLSERVER时有以下报错信息 打开SQL SERVER配置管理器,发现以下情况报错: 原因:由于先前安装了2005版VS,然后又安装了2015版VS 解决办法:卸载Loc ...

  9. SQL Server 2012复制教程以及复制的几种模式

    简介 SQL Server中的复制(Replication)是SQL Server高可用性的核心功能之一,在我看来,复制指的并不仅仅是一项技术,而是一些列技术的集合,包括从存储转发数据到同步数据到维护 ...

随机推荐

  1. Linux-(inotify-tools&rsync)

    inotifywait命令 mac中的是:fswatch,fsevents-tools. 1.命令格式: inotifywait [参数] [events] [targetDir] 2.命令功能: 平 ...

  2. Solidity类型转换

    类型转换,是一个十分重要,常用的手段. 一.隐式转换 隐式转换,就是当一个运算符能支持不同类型,编译器会隐式的尝试将一个操作数的类型,转为另一个操作数的类型,赋值同理. 条件是:值类型间的互相转换只要 ...

  3. js写的一个简单的手风琴菜单

    1 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&q ...

  4. UVa 442 Matrix Chain Multiplication(栈的应用)

    题目链接: https://cn.vjudge.net/problem/UVA-442 /* 问题 输入有括号表示优先级的矩阵链乘式子,计算该式进行的乘法次数之和 解题思路 栈的应用,直接忽视左括号, ...

  5. Java SDK夯住(Hang)问题排查

    夯住(Hang)是指程序仍在运行,卡在某个方法调用上,没有返回也没有异常抛出:卡住时间从几秒到几小时不等. Java程序发生Hang时,应该首先使用 jstack 把java进程的堆栈信息保存下来 , ...

  6. [转]论magento1和magento2的速度性能优化问题

    本文转自:http://www.360magento.com/blog/magento-speed-up/ magento从2007年发展至今,也经历了十余年的磨练,如今也迎来了magento的换代产 ...

  7. 记一次webapi传参数的问题

    .net小白一枚,经过了几个小时的研究,由于错误的写法导致后台始终接受不到前台传递过来的参数.首先看看控制器的参数 public Core.MVC.ServiceResult<DTO.Out.M ...

  8. [日常] Go语言圣经--Channel习题

    练习 8.3: 在netcat3例子中,conn虽然是一个interface类型的值,但是其底层真实类型是*net.TCPConn,代表一个TCP连接.一个TCP连接有读和写两个部分,可以使用Clos ...

  9. 设计模式之观察者模式(Observer)(4)

    简介 观察者模式(Observer)完美的将观察者和被观察的对象分离开.举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上.面向对象设 ...

  10. 【代码笔记】iOS-去掉NSString中的空格

    一,代码. - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, ...