137

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

  1. 如果是 1205446 则跳转到 1206306,如果是 1206321,则跳转到 1207306,如果是 1207314,则跳转到 1208306。如下图所示。

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

在此处输入图像描述

4

6 回答 6

171

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

现在默认情况下,在IDENTITY为列分配值并重新启动服务时使用 1,000 的缓存大小可能会“丢失”未使用的值( /int的缓存大小为 10,000 )。bigintnumeric

这在文档中提到

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

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

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

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

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

您应该知道,这些变通办法都不能保证没有间隙。这从来没有得到保证,IDENTITY因为它只能通过序列化插入到表中来实现。如果您需要无间隙色谱柱,您将需要使用不同的解决方案,IDENTITY或者SEQUENCE

于 2013-01-03T19:57:08.587 回答
60

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

解决方案是:

  • 运行SQL Server 配置管理器

  • 选择SQL Server 服务

    SQL Server 配置管理器

  • 右键单击SQL Server并选择Properties

  • Startup Parameters下的打开窗口中,键入-T272并单击Add,然后按Apply按钮并重新启动。

    SQL Server 启动参数

于 2014-10-03T21:02:57.393 回答
35

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

IDENTITY_CACHE = { 开 | 离开 }

在数据库级别启用或禁用身份缓存。默认值为开。身份缓存用于提高具有身份列的表的 INSERT 性能。为避免在服务器意外重新启动或故障转移到辅助服务器时出现标识列的值,请禁用 IDENTITY_CACHE 选项。 此选项类似于现有的 SQL Server Trace Flag 272,不同之处在于它可以在数据库级别设置,而不仅仅是在服务器级别。

(...)

G. 设置 IDENTITY_CACHE

此示例禁用身份缓存。

ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE=OFF ;
于 2017-04-19T19:39:32.853 回答
26

我知道我的回答可能迟到了。但我通过在 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';

如果您的桌子很少,这是一个好主意。但是如果你必须为许多表做,这种方法仍然有效,但不是一个好主意。

于 2015-03-30T23:31:21.293 回答
16

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

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

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

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

微软直接引入了一项重大更改,即他们破坏了一个不再按设计工作的工作 API,因为他们的系统在重新启动时忘记了他们当前的身份。缓存或不缓存,这是不可接受的,以 Bryan 为名的微软开发人员需要拥有它,而不是告诉世界它是“设计使然”和“功能”。当然,缓存是一项功能,但不知道下一个身份应该是什么,这不是一项功能。真是个可恶的BUG!!!

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

相反,这是我可耻的 hack(但不像微软引入的这个 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 行,你应该很高兴。它实际上不会对性能产生太大影响,即不会引起注意。

祝你好运。

于 2017-03-07T14:06:11.377 回答
8

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

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

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

于 2013-01-03T19:59:50.377 回答