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

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. js便签笔记(11)——浏览TOM大叔博客的学习笔记 part1

    1. 前言 这两天看了一下TOM大叔的<深入理解js系列>中的基础部分,根据自己的实际情况,做了读书笔记,记录了部分容易绊脚的问题.写篇文章,供大家分享. 2. 关于HTMLCollect ...

  2. logstash-3-输出到es中

    之前测试 filebeat和logstash的时候, 使用的是stdout标准输出, 现在我们想把数据输出到es中去, 1, 首先需要一个es: 修改配置文件 后台启动 ./bin/elasticse ...

  3. spring boot 自动更新静态文件和后台代码 -- 热部署

    在spring boot使用的过程中, 发现我修改了静态文件, 前台刷新后, 没有任何变化, 必须重新启动, 才能看到, 这简直不能让人接受. 那有什么方法来解决这个问题呢. Baidu之后, 得到了 ...

  4. xunsearch使用记录

    部署,配置,有时间在记录 <?php namespace APPlib; class XSGameku { public $error; public $xs; public $search; ...

  5. FFmpeg开发环境构建

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10035365.html 1. 相关资源介绍 本文主要讲述 linux 平台 x86(及x ...

  6. CAlayer一

    // // ViewController.m // Layer // // Created by City--Online on 15/4/9. // Copyright (c) 2015年 City ...

  7. Quartz2D指定显示范围

    在qq中,可以看到头像是圆形显示的,通过CGContextClip可以设置 CGContextRef context=UIGraphicsGetCurrentContext(); CGContextA ...

  8. [转]使用C#调用cmd来执行sql脚本

    本文转自:https://blog.csdn.net/tvmerp/article/details/1822669 下面是使用C#调用cmd来执行osql实现脚本的执行. using System; ...

  9. openssh升级到openssh-7.5p1踩坑

    环境:ubuntu 需要的安装包: http://zlib.net/   zlib 1.2.11最新版 http://www.linux-pam.org/library/   pam 1.3.0 ht ...

  10. 一卡通大冒险(hdu2512)

    一卡通大冒险 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Sub ...