标识增量在 SQL Server 数据库中跳转

Identity increment is jumping in SQL Server database

提问人:kashif 提问时间:1/4/2013 最后编辑:DhruvJoshikashif 更新时间:7/17/2023 访问量:89874

问:

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

  1. 如果是1205446,它跳到1206306,如果它是1206321,它跳到1207306,如果它是1207314,它跳到1208306。我想让您注意的是,每当跳跃发生时,最后三位数字保持不变,即 306,如下图所示。

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

enter image description here

sql sql-server sql-server-2012 标识列

评论

0赞 Taryn 1/4/2013
如果添加到查询中,这些记录真的不存在吗?您确定在插入记录时没有错误吗?如果尝试插入记录但失败,则标识将递增,如果删除记录也是如此。如果删除记录,则不会重置。你能为表发布创建表吗?order by ReceiptNoReceiptNoFee
4赞 Andrew 1/4/2013
第一个问题是 - 为什么这很重要?它应该是任意的唯一 ID
1赞 Martin Smith 1/4/2013
这是在服务器上运行还是在桌面上快速运行?想知道为什么服务似乎如此频繁地重新启动?
0赞 kashif 1/4/2013
@bluefeet我知道错误发生时,就会发生身份递增。我 100% 确定没有错误。我通过添加表和我用来插入行的存储过程来编辑我的问题。
0赞 Martin Smith 1/4/2013
@kashif - 99% 确定不需要。正好跳跃 1,000 (, , ) 意味着连接项线程中的解释几乎可以肯定适用。120630612073061207806

答:

185赞 Martin Smith 1/4/2013 #1

由于自 SQL Server 2012 以来的性能改进,您遇到此行为。

现在,当为列分配值时,它默认使用 1,000 的缓存大小,重新启动服务可能会“丢失”未使用的值(/ 的缓存大小为 10,000)。IDENTITYintbigintnumeric

这在文档中有所提及

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

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

除非您以异常频繁的频率重新启动服务,否则任何“丢失”的值都不太可能对数据类型允许的值范围产生任何重大影响,因此最好的策略是不要担心它。

如果由于某种原因这对您来说是一个真正的问题,那么一些可能的解决方法是......

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

您应该知道,这些解决方法都不能保证没有差距。IDENTITY 从未保证过这一点,因为只有将插入序列化到表中才能实现(如果没有这种序列化,标识值可以分配给最终失败的插入语句 - 例如,由于违反约束。或者简单回滚的事务)。如果您需要真正无间隙的列,则需要使用与 IDENTITYSEQUENCE 不同的解决方案(例如

评论

1赞 kashif 1/4/2013
为了验证你所说的,我插入了一些值并得到了1208309,1208310然后我重新启动了服务器,然后当我添加该行时,我得到了1209309这意味着你说的是绝对正确的。多谢。现在你能告诉我如何解决这个问题吗?你会建议我使用SQL Server 2008而不是我以前使用的2012,甚至使用2012,这个问题可以解决吗?
1赞 Martin Smith 1/4/2013
@kashif - 这对你来说真的是个问题吗?即使您每天用完 1,000 个标识值,仍需要 200 万天才能用完这些值。如果确实需要旧行为,可以将 SQL Server 设置为使用跟踪标志 272 启动,也可以使用 a 而不是 ,并将 Sequence 设置为缓存大小为 。SEQUENCEIDENTITY0
0赞 kashif 1/4/2013
对于我的解决方案,我从您那里得到了相当令人印象深刻和令人满意的答案,非常感谢。
0赞 Martin Smith 1/5/2013
实际上,从你那里,我看到你正在使用并开始编号,这意味着如果你每天使用 24 个,你会在几天(1,000 年)后用完。CREATE TABLEnumeric(7)12000018,799
1赞 Chris Nevill 2/9/2016
我正在运行我们在野外拥有的应用程序的调试实例。我遇到了这种情况,我差点心脏病发作,以为我不知何故无意中在实时服务器上工作!谢谢你。对:)的恐慌
8赞 Sebastian Meine 1/4/2013 #2

跳跃标识值的可能原因有很多。它们的范围从回滚插入到用于复制的身份管理。是什么原因导致了这种情况,除非在您的系统中花费一些时间,我就无法判断。

但是,您应该知道,在任何情况下都不能假定标识列是连续的。有太多的事情会导致差距。

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

63赞 Harun ERGUL 10/4/2014 #3

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

解决方案是:

  • 运行 SQL Server 配置管理器

  • 选择“SQL Server 服务”。

    SQL Server Configuration Manager

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

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

    SQL Server startup parameters

评论

1赞 Fragment 2/13/2015
这个方法真的很有效,非常感谢!如此所述,此问题不会在 SQL Server 2012 及其 Service Pack 中修复,只会在下一个版本中修复。
2赞 Ege Ersoz 7/8/2015
有没有办法将跟踪标志应用于单个数据库?我不想在整个服务器上进行此更改,因为我有第三方数据库,我不确定这将如何影响它们。
1赞 Savage 3/17/2016
我没有遵循原因,但显然有些用户不得不使用小写的“t”才能让它工作。请参阅上面评论中 Fragment 发布的链接。
27赞 Jeyara 3/31/2015 #4

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

在 master DB 中创建以下存储过程。

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';

如果您的表很少,这是一个好主意。但是,如果您必须对许多表执行操作,此方法仍然有效,但不是一个好主意。

评论

0赞 rom 10/2/2019
好的想法。但它不适用于依赖表,是吗?我的意思是,它是否修复了外键值?
0赞 Jeyara 10/2/2019
@rom5jp 修复 FK 不是这个答案的重点。这一切都是为了修复表的下一个可能的 PK 值。只要 MAX(id) 不在任何 FK 中,它就应该起作用。
1赞 Leif Neland 2/16/2023
当税务人员问:“这 10000 张发票怎么了?虽然如果只缺少几个 ID,那就更可疑了。因此,至少应该对关键 ID 进行解决方法。
17赞 green_mystic_Orb 3/7/2017 #5

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

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

此外,如果您有将这些标识列用于主键的现有表,则删除这些列并重新创建新列以使用 BS 序列解决方法是一项巨大的努力。仅当您在 SQL 2012+ 中从头开始设计新表时,序列解决方法才有效

底线是,如果您使用的是 SQL Server 2008R2,那么请继续使用它。说真的,坚持下去。直到 Microsoft 承认他们引入了一个巨大的错误,即使在 Sql Server 2016 中仍然存在,那么在他们拥有它并修复它之前,我们不应该升级。

Microsoft直接引入了一个重大更改,即他们破坏了一个不再按设计工作的工作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)

只需插入前的那 3 行,您就可以开始了。它真的不会对性能产生太大影响,也就是说,它不会被注意到。

祝你好运。

37赞 Lukasz Szozda 4/20/2017 #6

SQL Server 2017+ 中,可以使用 ALTER DATABASE SCOPED CONFIGURATION:

IDENTITY_CACHE = { ON |关闭 }

在数据库级别启用或禁用标识缓存。默认 标识缓存用于提高 具有 Identity 列的表。为了避免 标识列(在服务器意外重新启动的情况下)或 故障转移到辅助服务器,禁用IDENTITY_CACHE选项。此选项类似于现有的 SQL Server 跟踪标志 272。 除了它可以在数据库级别设置,而不仅仅是在 服务器级别。

(...)

G. 设置IDENTITY_CACHE

此示例禁用标识缓存。

ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE=OFF ;