El incremento de identidad está saltando en la base de datos de SQL Server

126

En una de mis tablas Feeen la columna "ReceiptNo" en el incremento de identidad de la base de datos de SQL Server 2012, de repente comenzó a saltar a 100 en lugar de 1, dependiendo de las siguientes dos cosas.

  1. si es 1205446, salta a 1206306, si es 1206321, salta a 1207306 y si es 1207314, salta a 1208306. Lo que quiero que note es que los últimos tres dígitos permanecen constantes, es decir, 306 cada vez que salta ocurre como se muestra en la siguiente imagen.

  2. Este problema ocurre cuando reinicio mi computadora

ingrese la descripción de la imagen aquí

kashif
fuente
Si agrega order by ReceiptNoa su consulta, ¿esos registros realmente no están allí? ¿Está seguro de que cuando se insertan registros no hay errores? Si un registro intenta insertarse y falla, la identidad aumentará, lo mismo si se eliminan los registros. Si se eliminan los registros, ReceiptNono se restablece. ¿Puedes publicar la tabla de creación para la Feetabla?
Taryn
44
La primera pregunta es: ¿por qué es importante? debería ser una identificación única arbitraria
Andrew
1
¿Se está ejecutando en un servidor o tal vez se expresa en un escritorio? ¿Se pregunta por qué parece que el servicio se reinicia con tanta frecuencia?
Martin Smith
@bluefeet Sé que cuando se produce el error, se produce un incremento de identidad. Estoy 100% seguro de que no hay errores. Estoy editando mi pregunta agregando tabla y el procedimiento almacenado que utilizo para insertar las filas.
kashif
@kashif: 99% seguro de que no es necesario. Los saltos de exactamente 1.000 ( 1206306, 1207306, 1207806) significa la explicación de la rosca de artículos Conectar casi seguro que se aplica.
Martin Smith

Respuestas:

158

Se encuentra con este comportamiento debido a una mejora en el rendimiento desde SQL Server 2012.

Ahora, por defecto, usa un tamaño de caché de 1,000 cuando asigna IDENTITYvalores para una intcolumna y reinicia el servicio puede "perder" los valores no utilizados (el tamaño de caché es 10,000 para bigint/ numeric).

Esto se menciona en la documentación.

SQL Server puede almacenar en caché los valores de identidad por razones de rendimiento y algunos de los valores asignados pueden perderse durante una falla de la base de datos o el reinicio del servidor. Esto puede provocar lagunas en el valor de identidad al insertar. Si las brechas no son aceptables, la aplicación debe usar su propio mecanismo para generar valores clave. El uso de un generador de secuencia con la NOCACHEopción puede limitar las brechas a las transacciones que nunca se confirman.

Según los datos que ha mostrado, parece que esto sucedió después de la entrada de datos del 22 de diciembre y luego, cuando reinició, SQL Server reservó los valores 1206306 - 1207305. Después de la entrada de datos del 24 al 25 de diciembre, se realizó otro reinicio y SQL Server reservó el siguiente rango 1207306 - 1208305visible en las entradas para el 28.

A menos que reinicie el servicio con una frecuencia inusual, es poco probable que los valores "perdidos" hagan mella significativa en el rango de valores permitidos por el tipo de datos, por lo que la mejor política es no preocuparse por ello.

Si por alguna razón esto es un problema real para usted, algunas posibles soluciones son ...

  1. Puede usar una SEQUENCEcolumna de identidad en lugar de una y definir un tamaño de caché más pequeño, por ejemplo, y usar NEXT VALUE FORen una columna predeterminada.
  2. O aplique el indicador de rastreo 272 que hace que la IDENTITYasignación se registre como en versiones hasta 2008 R2. Esto se aplica globalmente a todas las bases de datos.
  3. O, para versiones recientes, ejecute ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFFpara deshabilitar el almacenamiento en caché de identidad para una base de datos específica.

Debe tener en cuenta que ninguna de estas soluciones alternativas no garantiza lagunas. Esto nunca ha sido garantizado IDENTITYya que solo sería posible serializando inserciones en la tabla. Si necesita una columna sin espacios en blanco que tendrá que utilizar una solución diferente a cualquiera IDENTITYoSEQUENCE

Martin Smith
fuente
1
para verificar lo que dijiste, inserté algunos valores y obtuve 1208309, 1208310 y luego reinicié el servidor y luego, cuando agregué la fila, obtuve 1209309, eso significa que lo que dijiste es absolutamente correcto. muchas gracias. ahora puedes decirme cómo puedo resolver este problema. ¿me sugieres que use sql server 2008 en lugar de 2012 que estaba usando anteriormente o incluso usando 2012, este problema se puede resolver?
kashif
1
@kashif - ¿Es realmente un problema para ti? Incluso si usa hasta 1,000 valores de identidad por día, todavía tomará 2 millones de días antes de que se acaben los valores. Si desea el comportamiento anterior, puede configurar SQL Server para que se inicie con el indicador de rastreo 272 o puede usar un en SEQUENCElugar de un IDENTITYy configurar la secuencia para que tenga un tamaño de caché de 0.
Martin Smith
Obtuve respuestas bastante impresionantes y satisfactorias de usted para mi solución, muchas gracias.
kashif
En realidad, por su parte CREATE TABLE, veo que está usando numeric(7)y ha comenzado la numeración, lo 1200001que significa que se quedaría sin 8,799días (24 años) si usa 1,000 por día.
Martin Smith
Se supone que el valor real "saltado" depende del tipo de columna utilizada. Por ejemplo, una big intcolumna generalmente "saltará" en 10,000 por reinicio.
StarPilot
60

Este problema se produce después de reiniciar el servidor SQL.

La solucion es:

  • Ejecute el Administrador de configuración de SQL Server .

  • Seleccione Servicios de SQL Server .

    Administrador de configuración de SQL Server

  • Haga clic con el botón derecho en SQL Server y seleccione Propiedades .

  • En la ventana de apertura en Parámetros de inicio , escriba -T272y haga clic en Agregar , luego presione el botón Aplicar y reinicie.

    Parámetros de inicio de SQL Server

Harun ERGUL
fuente
1
Este método realmente funciona, ¡muchas gracias! y como se dijo aquí , este problema no se solucionará en SQL Server 2012, y en sus paquetes de servicio, solo en la próxima versión.
Fragmento del
2
¿Hay alguna manera de aplicar el indicador de rastreo a bases de datos individuales? No quiero hacer este cambio en todo el servidor porque tengo bases de datos de terceros y no estoy seguro de cómo esto los afectará.
Ege Ersoz
1
No seguí las razones, pero aparentemente algunos usuarios tuvieron que usar minúsculas "t" para que funcionara. Vea el enlace publicado por Fragment en el comentario anterior.
Salvaje
33

Desde SQL Server 2017+usted podría usar ALTER DATABASE SCOPED CONFIGURATION :

IDENTITY_CACHE = {ON | APAGADO }

Habilita o deshabilita el caché de identidad en el nivel de la base de datos. El valor predeterminado es ON. El almacenamiento en caché de identidad se utiliza para mejorar el rendimiento de INSERT en tablas con columnas de identidad. Para evitar lagunas en los valores de la columna Identidad en los casos en que el servidor se reinicia inesperadamente o falla en un servidor secundario, desactive la opción IDENTITY_CACHE. Esta opción es similar al indicador de seguimiento de SQL Server 272 existente, excepto que se puede establecer en el nivel de la base de datos en lugar de solo en el nivel del servidor.

(...)

G. Establecer IDENTITY_CACHE

Este ejemplo deshabilita el caché de identidad.

ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE=OFF ;
Lukasz Szozda
fuente
25

Sé que mi respuesta podría llegar tarde a la fiesta. Pero he resuelto de otra manera agregando un procedimiento almacenado de inicio en SQL Server 2012.

Cree un siguiente procedimiento almacenado en la base de datos maestra.

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

Luego agréguelo a Inicio usando la siguiente sintaxis.

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

Esta es una buena idea si tiene pocas tablas. pero si tiene que hacerlo para muchas tablas, este método aún funciona pero no es una buena idea.

Jeyara
fuente
Buena idea. Pero no funciona para las tablas dependientes, ¿verdad? Quiero decir, ¿fija los valores de la clave externa?
rom5jp
@ rom5jp La fijación de FK no es el punto de esta respuesta. Se trata de arreglar el posible próximo valor PK de una tabla. Mientras MAX (id) no esté en ninguno de los FK, debería funcionar.
Jeyara
14

Este sigue siendo un problema muy común entre muchos desarrolladores y aplicaciones, independientemente de su tamaño.

Lamentablemente, las sugerencias anteriores no solucionan todos los escenarios, es decir, alojamiento compartido, no puede confiar en su host para establecer el parámetro de inicio -t272.

Además, si tiene tablas existentes que usan estas columnas de identidad para las claves primarias, es un ENORME esfuerzo eliminar esas columnas y recrear otras nuevas para usar la solución alternativa de la secuencia BS. La solución alternativa de secuencia solo es buena si está diseñando tablas nuevas desde cero en SQL 2012+

La conclusión es que, si está en SQL Server 2008R2, MANTÉNGASE EN ELLO. En serio, quédate ahí. Hasta que Microsoft admita que introdujeron un error ENORME, que todavía está allí incluso en Sql Server 2016, no deberíamos actualizarlo hasta que lo posean y lo FIJEN.

Microsoft introdujo un cambio radical, es decir, rompieron una API funcional que ya no funciona según lo diseñado, debido a que su sistema olvida su identidad actual en un reinicio. Caché o no caché, esto es inaceptable, y el desarrollador de Microsoft con el nombre de Bryan necesita ser el propietario, en lugar de decirle al mundo que es "por diseño" y una "característica". Claro, el almacenamiento en caché es una característica, pero perder la noción de cuál debería ser la próxima identidad NO ES UNA CARACTERÍSTICA. Es un error!

Compartiré la solución alternativa que utilicé, porque Mis bases de datos están en servidores de alojamiento compartido, además, no estoy soltando y recreando mis columnas de Clave primaria, eso sería una gran PITA.

En cambio, este es mi truco vergonzoso (pero no tan vergonzoso como este error POS que ha introducido Microsoft).

Hack / Fix:

Antes de los comandos de inserción, simplemente reinicialice su identidad antes de cada inserción. Esta solución solo se recomienda si no tiene control de administrador sobre su instancia de SQL Server, de lo contrario, sugiero volver a reiniciar el servidor.

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)

Solo esas 3 líneas inmediatamente antes de su inserción, y debería estar listo para comenzar. Realmente no afectará tanto el rendimiento, es decir, pasará desapercibido.

Buena suerte.

green_mystic_Orb
fuente
7

Hay muchas razones posibles para saltar valores de identidad. Van desde inserciones revertidas hasta gestión de identidad para la replicación. No puedo decir qué está causando esto en su caso sin pasar algún tiempo en su sistema.

Sin embargo, debe saber que en ningún caso puede suponer que una columna de identidad sea contigua. Hay demasiadas cosas que pueden causar lagunas.

Puede encontrar un poco más de información sobre esto aquí: http://sqlity.net/en/792/the-gap-in-the-identity-value-sequence/

Sebastian Meine
fuente