SQL Server数据库中的身份增量跃升


126

在我的其中一个表Fee中,SQL Server 2012数据库标识增量的列“ ReceiptNo”突然开始跳到100s,而不是1,具体取决于以下两件事。

  1. 如果是1205446,则跳至1206306;如果是1206321,则跳至1207306;如果是1207314,则跳至1208306。如下图所示发生。

  2. 重新启动计算机时会出现此问题

在此处输入图片说明


如果添加order by ReceiptNo到查询中,这些记录真的不存在吗?您确定在插入记录时没有错误吗?如果记录试图插入而失败,则身份将增加,如果删除记录,则相同。如果删除记录,ReceiptNo则不会重置。您可以过帐该表的创建Fee表吗?
塔林

4
第一个问题是-为什么重要?它应该是一个任意的唯一ID
Andrew

1
它是在服务器上运行还是在台式机上表达?想知道为什么似乎频繁重启服务?
马丁·史密斯

@bluefeet我知道错误发生时,身份会增加。我100%肯定没有错误。我通过添加表和用于插入行的存储过程来编辑我的问题。
kashif

@kashif-99%确保不需要。恰好1000的跳跃(120630612073061207806)指连接项目主题的解释几乎肯定适用。
马丁·史密斯

Answers:


158

由于SQL Server 2012年以来的性能改进,您遇到此问题。

现在,默认情况下,当IDENTITYint列分配值并且重新启动服务可以“丢失”未使用的值时,默认情况下使用的缓存大小为1,000 (对于bigint/numeric)。

文档中提到了这一点

由于性能原因,SQL Server可能会缓存标识值,并且在数据库故障或服务器重新启动期间,某些分配的值可能会丢失。这可能导致插入时身份值出现空白。如果差距不可接受,则应用程序应使用自己的机制来生成键值。将序列生成器与该NOCACHE选项一起使用可以将差距限制为从未提交的事务。

从您显示的数据来看,这似乎发生在12月22日的数据输入之后,然后在重新启动时SQL Server保留了这些值1206306 - 1207305。在完成12月24日至25日的数据输入后,再次重新启动,SQL Server保留了下一个范围1207306 - 1208305了第28个条目中可见。

除非您以异常的频率重新启动服务,否则任何“丢失”的值都不太可能在数据类型所允许的值范围内产生任何明显的损失,因此最好的策略是不必为此担心。

如果出于某些原因这是一个真正的问题,那么可能的解决方法是...

  1. 您可以使用SEQUENCE而不是标识列,并定义较小的缓存大小,例如,并NEXT VALUE FOR在列默认值中使用。
  2. 或应用跟踪标志272,该标志使IDENTITY分配记录的版本一直到2008 R2。这在全局上适用于所有数据库。
  3. 或者,对于最新版本,执行ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFF以禁用特定数据库的身份缓存。

您应该知道,这些变通办法都不能保证没有任何差距。这从来没有得到保证,IDENTITY因为只有将插入序列化到表中才有可能。如果你需要一个无缝列,您需要使用不同的解决方案比任何IDENTITYSEQUENCE


1
为了验证您说的是什么,我插入了一些值并得到1208309、1208310,然后重新启动服务器,然后在添加行时得到了1209309,这意味着您说的是绝对正确的。非常感谢。现在您能告诉我如何解决这个问题吗?您是否建议我使用sql server 2008而不是我以前使用过的2012,甚至可以使用2012这个问题都可以解决?
kashif

1
@kashif-这实际上对您有问题吗?即使您每天使用1,000个身份值,仍然需要200万天才能用完所有值。如果确实想要旧的行为,则可以将SQL Server设置为使用跟踪标志272启动,或者可以使用a SEQUENCE代替,IDENTITY并将Sequence设置为具有的缓存大小0
马丁·史密斯

非常感谢您为我的解决方案给您留下深刻印象和满意的答复。
kashif

实际上,从CREATE TABLE我看来,您正在使用numeric(7)并开始编号,1200001这意味着8,799如果每天使用1,000,那么几天(24年)后您就会用光。
马丁·史密斯

实际值“跳跃”应该取决于所使用的列类型。例如,一big int列通常每次重新启动会“跳” 10,000。
StarPilot 2014年

60

重新启动SQL Server后,会出现此问题。

解决方案是:

  • 运行SQL Server配置管理器

  • 选择“ SQL Server服务”

    SQL Server配置管理器

  • 右键单击“ SQL Server”,然后选择“ 属性”

  • 在“ 启动参数”下打开的窗口中,键入-T272并单击“ 添加”,然后按“ 应用”按钮并重新启动。

    SQL Server启动参数


1
此方法确实有效,非常感谢!并且如此处所述,此问题将不会在SQL Server 2012及其Service Pack中得到解决-仅在下一版本中。
片段

2
有没有一种方法可以将跟踪标志应用于单个数据库?我不想在整个服务器上进行此更改,因为我拥有第三方数据库,并且不确定这将如何影响它们。
Ege Ersoz

1
我没有遵循这些原因,但是显然有些用户必须使用小写的“ t”才能使其正常工作。请参阅上面评论中Fragment发布的链接。
野人

33

SQL Server 2017+您可以使用ALTER DATABASE SCOPED CONFIGURATION

IDENTITY_CACHE = {开| 关闭}

在数据库级别启用或禁用身份缓存。默认为开。身份缓存用于提高具有“身份”列的表的INSERT性能。为避免在服务器意外重新启动或故障转移到辅助服务器的情况下,Identity列的值出现间隙,请禁用IDENTITY_CACHE选项。 此选项与现有的SQL Server跟踪标志272相似,不同之处在于可以在数据库级别而不是仅在服务器级别进行设置。

(...)

G.设置IDENTITY_CACHE

本示例禁用身份缓存。

ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE=OFF ;

25

我知道我的回答可能迟到了。但是我已经通过在SQL Server 2012中添加启动存储过程以另一种方式解决了。

在主数据库中创建以下存储过程。

USE [master]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[ResetTableNameIdentityAfterRestart]
AS
BEGIN

begin TRAN
    declare @id int = 0
    SELECT @id =  MAX(id) FROM [DatabaseName].dbo.[TableName]
    --print @id
    DBCC CHECKIDENT ('[DatabaseName].dbo.[TableName]', reseed, @id)
Commit

END

然后使用以下语法将其添加到“启动”中。

EXEC sp_procoption 'ResetOrderIdentityAfterRestart', 'startup', 'on';

如果表很少,这是个好主意。但是如果您必须处理许多表,则此方法仍然有效,但不是一个好主意。


好的想法。但这不适用于从属表,对吗?我的意思是,它不解决外键值吗?
rom5jp

@ rom5jp修复FK并非此答案的重点。其全部内容与确定表的下一个PK值有关。只要MAX(id)不在任何FK中,它就应该起作用。
Jeyara

14

无论大小,这在许多开发人员和应用程序中仍然是一个非常普遍的问题。

不幸的是,以上建议并不能解决所有情况,即共享主机,您不能依靠主机来设置-t272启动参数。

另外,如果您有使用这些标识列作为主键的现有表,则要删除这些列并重新创建新列以使用BS序列变通办法,这是一项巨大的工作。仅当您在SQL 2012+中从头开始设计新表时,Sequence解决方法才是好方法

最重要的是,如果您使用的是Sql Server 2008R2,则请继续使用。认真地,坚持下去。直到Microsoft承认他们引入了一个巨大的bug(即使在Sql Server 2016中仍然存在),我们才应该升级,直到他们拥有它并对其进行修复。

微软直截了当地提出了一项重大更改,即,由于系统重新启动时忘记了当前身份,他们中断了一个不再按设计工作的API。缓存或不缓存,这是不可接受的,以Bryan的名义命名的Microsoft开发人员需要拥有它,而不是向世人表明它是“设计”和“功能”的。当然,缓存是一项功能,但是无法掌握下一个标识应该不是什么功能。这是一个令人毛骨悚然的臭虫!

我将分享我使用的变通方法,因为我的数据库位于共享主机服务器上,而且,我不会删除并重新创建主键列,那将是一个巨大的PITA。

取而代之的是,这是我可耻的黑客(但不像微软引入的此POS错误那样可耻)。

破解/修复:

在执行插入命令之前,只需在每次插入之前重新设置您的身份即可。仅当您没有对Sql Server实例的管理员控制权时,才建议使用此修复程序,否则,建议在重新启动服务器时重新播种。

declare @newId int -- where int is the datatype of your PKey or Id column
select @newId = max(YourBuggedIdColumn) from YOUR_TABLE_NAME
DBCC CheckIdent('YOUR_TABLE_NAME', RESEED, @newId)

仅在插入之前的那三行,您应该就可以开始了。它确实不会对性能造成太大影响,即不会引起注意。

祝好运。


7

跳跃身份值有许多可能的原因。它们的范围从回滚的插入到用于复制的身份管理。在您的情况下,这是由什么引起的,如果您不花一些时间在您的系统中就无法分辨。

但是,您应该知道,在任何情况下都不能假定Identity列是contiguos。太多的事情可能导致差距。

您可以在此处找到有关此内容的更多信息:http : //sqlity.net/zh/792/the-gap-in-the-identity-value-sequence/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.