O incremento de identidade está saltando no banco de dados do SQL Server

126

Em uma das minhas tabelas Feeda coluna "ReceiptNo" no SQL Server 2012, o incremento de identidade do banco de dados começou subitamente a saltar para 100s em vez de 1, dependendo das duas coisas a seguir.

  1. se for 1205446, saltar para 1206306, se for 1206321, saltar para 1207306 e se for 1207314, saltar para 1208306. O que quero fazer você notar é que os últimos três dígitos permanecem constantes, ou seja, 306 sempre que o salto ocorre como mostrado na figura a seguir.

  2. esse problema ocorre quando eu reinicio o computador

insira a descrição da imagem aqui

kashif
fonte
Se você adicionar order by ReceiptNoà sua consulta, esses registros realmente não estão lá? Tem certeza de que quando os registros estão sendo inseridos, não há erros? Se um registro tentar ser inserido e falhar, a identidade será incrementada, o mesmo se os registros forem excluídos. Se os registros forem excluídos, ReceiptNonão será redefinido. Você pode postar a tabela de criação para a Feetabela?
Taryn
4
A primeira pergunta é - por que isso importa? deve ser um ID único e arbitrário #
Andrew
1
Isso está sendo executado em um servidor ou talvez seja expresso em uma área de trabalho? Querendo saber por que parece que o serviço é reiniciado com tanta frequência?
Martin Smith
@ bluefeet Eu sei que quando o erro ocorre, o incremento da identidade ocorre. Estou 100% certo de que não há erros. Estou editando minha pergunta adicionando a tabela e o procedimento armazenado que eu uso para inserir as linhas.
kashif
@kashif - 99% de certeza de que não é necessário. A salta por exatamente 1.000 ( 1206306, 1207306, 1207806) significa a explicação no tópico do item Ligação quase certamente se aplica.
Martin Smith

Respostas:

158

Você está enfrentando esse comportamento devido a uma melhoria de desempenho desde o SQL Server 2012.

Agora, por padrão, usa um tamanho de cache de 1.000 ao alocar IDENTITYvalores para uma intcoluna e reiniciar o serviço pode "perder" valores não utilizados (o tamanho do cache é de 10.000 para bigint/ numeric).

Isso é mencionado na documentação

O SQL Server pode armazenar em cache os valores de identidade por motivos de desempenho e alguns dos valores atribuídos podem ser perdidos durante uma falha no banco de dados ou na reinicialização do servidor. Isso pode resultar em lacunas no valor da identidade após a inserção. Se as lacunas não forem aceitáveis, o aplicativo deve usar seu próprio mecanismo para gerar valores-chave. O uso de um gerador de sequência com a NOCACHEopção pode limitar as lacunas às transações que nunca são confirmadas.

A partir dos dados que você mostrou, parece que isso aconteceu após a entrada de dados de 22 de dezembro e, quando reiniciou o SQL Server, reservou os valores 1206306 - 1207305. Após a entrada de dados de 24 a 25 de dezembro, foi feita outra reinicialização e o SQL Server reservou o próximo intervalo 1207306 - 1208305visível nas entradas do dia 28.

A menos que você esteja reiniciando o serviço com frequência incomum, é improvável que quaisquer valores "perdidos" afetem significativamente o intervalo de valores permitido pelo tipo de dados, portanto, a melhor política é não se preocupar com isso.

Se, por algum motivo, isso for um problema real, algumas soluções possíveis são:

  1. Você pode usar uma SEQUENCEcoluna em vez de uma identidade e definir um tamanho de cache menor, por exemplo, e usar NEXT VALUE FORem uma coluna padrão.
  2. Ou aplique o sinalizador de rastreamento 272 que faz a IDENTITYalocação registrada como nas versões até 2008 R2. Isso se aplica globalmente a todos os bancos de dados.
  3. Ou, para versões recentes, execute ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFFpara desativar o cache de identidade de um banco de dados específico.

Você deve estar ciente de que nenhuma dessas soluções alternativas garante nenhuma lacuna. Isso nunca foi garantido IDENTITY, pois somente seria possível serializando inserções na tabela. Se você precisar de uma coluna gapless você vai precisar usar uma solução diferente do que qualquer um IDENTITYouSEQUENCE

Martin Smith
fonte
1
para verificar o que você disse, inseri alguns valores e obtive 1208309, 1208310, reiniciei o servidor e, quando adicionei a linha, obtive 1209309, o que você disse está absolutamente certo. Muito obrigado. Agora você pode me dizer como posso resolver esse problema. você me sugeriria usar o sql server 2008 em vez de 2012 que eu estava usando anteriormente ou mesmo usando 2012 esse problema pode ser resolvido?
kashif
1
@ kashif - É realmente um problema para você? Mesmo se você usar 1.000 valores de identidade por dia, ainda levará 2 milhões de dias para ficar sem valores. Se você deseja o comportamento antigo, pode configurar o SQL Server para inicializar com o sinalizador de rastreamento 272 ou pode usar um em SEQUENCEvez de um IDENTITYe definir a sequência para ter um tamanho de cache igual a 0.
Martin Smith
Recebi respostas bastante impressionantes e satisfatórias de você pela minha solução, muito obrigado.
kashif
Na verdade, CREATE TABLEvejo que você está usando numeric(7)e começou a numeração, o 1200001que significa que você acabaria após 8,799dias (24 anos) se usasse 1.000 por dia.
Martin Smith
O valor real "saltado" deve ser dependente do tipo de coluna usado. Por exemplo, uma big intcoluna geralmente "aumenta" em 10.000 por reinicialização.
StarPilot
60

Esse problema ocorre após reiniciar o SQL Server.

A solução é:

  • Execute o SQL Server Configuration Manager .

  • Selecione Serviços do SQL Server .

    Gerenciador de Configuração do SQL Server

  • Clique com o botão direito do mouse em SQL Server e selecione Propriedades .

  • Na janela de abertura, em Parâmetros de inicialização , digite -T272e clique em Adicionar , pressione o botão Aplicar e reinicie.

    Parâmetros de inicialização do SQL Server

Harun ERGUL
fonte
1
Este método é realmente trabalho, muito obrigado! e, como dito aqui , esse problema não será corrigido no SQL Server 2012 e nos service packs, apenas na próxima versão.
Fragmento
2
Existe uma maneira de aplicar o sinalizador de rastreamento a bancos de dados individuais? Não quero fazer essa alteração em todo o servidor, porque tenho bancos de dados de terceiros e não tenho certeza de como isso os afetará.
Ege Ersoz
1
Eu não segui os motivos, mas aparentemente alguns usuários tiveram que usar letras minúsculas "t" para fazê-lo funcionar. Veja o link postado por Fragment no comentário acima.
Savage
33

De SQL Server 2017+você poderia usar ALTER DATABASE SCOPED CONFIGURATION :

IDENTITY_CACHE = {LIGADO | FORA }

Habilita ou desabilita o cache de identidade no nível do banco de dados. O padrão é ON. O cache de identidade é usado para melhorar o desempenho do INSERT em tabelas com colunas de identidade. Para evitar falhas nos valores da coluna Identidade nos casos em que o servidor reinicia inesperadamente ou realiza failover em um servidor secundário, desative a opção IDENTITY_CACHE. Essa opção é semelhante ao sinalizador de rastreamento do SQL Server 272 existente, exceto pelo fato de poder ser definido no nível do banco de dados e não apenas no nível do servidor.

(...)

G. Defina IDENTITY_CACHE

Este exemplo desabilita o cache de identidade.

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

Eu sei que minha resposta pode estar atrasada para a festa. Mas resolvi de outra maneira adicionando um procedimento armazenado de inicialização no SQL Server 2012.

Crie um procedimento armazenado a seguir no banco de dados mestre.

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

Em seguida, adicione-o à Inicialização usando a seguinte sintaxe.

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

Essa é uma boa ideia se você tiver poucas tabelas. mas se você precisar fazer isso para muitas tabelas, esse método ainda funcionará, mas não é uma boa ideia.

Jeyara
fonte
Boa ideia. Mas não funciona para as tabelas dependentes, funciona? Quero dizer, você corrige os valores das chaves estrangeiras?
Rom5jp 01/10/19
@ rom5jp Fixar FK não é o objetivo desta resposta. É tudo sobre como corrigir o possível próximo valor PK de uma tabela. Desde que MAX (id) não esteja em nenhum dos FK, ele deve funcionar.
Jeyara 02/10/19
14

Esse ainda é um problema muito comum entre muitos desenvolvedores e aplicativos, independentemente do tamanho.

Infelizmente, as sugestões acima não corrigem todos os cenários, ou seja, hospedagem compartilhada, você não pode confiar no seu host para definir o parâmetro de inicialização -t272.

Além disso, se você possui tabelas existentes que usam essas colunas de identidade para chaves primárias, é um enorme esforço para eliminar essas colunas e recriar novas para usar a solução alternativa da sequência BS. A solução alternativa de Sequência só é boa se você estiver criando as tabelas novas do zero no SQL 2012+

A linha inferior é, se você estiver no Sql Server 2008R2, FIQUE LIGADO. Sério, fique nele. Até a Microsoft admitir que eles introduziram um bug ENORME, que ainda existe no Sql Server 2016, não devemos atualizar até que eles sejam os proprietários e o CORRETAMENTE.

A Microsoft introduziu imediatamente uma mudança de quebra, ou seja, quebrou uma API funcional que não funciona mais como projetada, devido ao fato de que seu sistema esquece sua identidade atual em uma reinicialização. Cache ou sem cache, isso é inaceitável, e o desenvolvedor da Microsoft, com o nome de Bryan, precisa possuí-lo, em vez de dizer ao mundo que é "por design" e um "recurso". Certamente, o armazenamento em cache é um recurso, mas perder a noção do que deve ser a próxima identidade NÃO É UM RECURSO. É um erro fricken !!!

Compartilharei a solução alternativa que usei, porque os Meus DBs estão em servidores de Hospedagem Compartilhada e também não estou descartando e recriando minhas colunas de Chave Primária, que seria uma PITA enorme.

Em vez disso, esse é meu truque vergonhoso (mas não tão vergonhoso quanto esse bug do POS que a microsoft introduziu).

Hack / Correção:

Antes dos comandos de inserção, basta reenviar sua identidade antes de cada inserção. Essa correção é recomendada apenas se você não tiver controle de administrador sobre sua instância do Sql Server; caso contrário, sugiro que continue com a reinicialização do 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)

Apenas essas três linhas imediatamente antes da inserção e você deve estar pronto. Realmente não afetará muito o desempenho, ou seja, será imperceptível.

Boa sorte.

green_mystic_Orb
fonte
7

Existem muitas razões possíveis para saltar valores de identidade. Eles variam de inserções revertidas ao gerenciamento de identidades para replicação. O que está causando isso no seu caso, não posso dizer sem passar algum tempo no seu sistema.

No entanto, você deve saber que, em nenhum caso, você pode assumir que uma coluna de identidade é contiguosa. Existem muitas coisas que podem causar lacunas.

Você pode encontrar um pouco mais de informações sobre isso aqui: http://sqlity.net/en/792/the-gap-in-the-identity-value-sequence/

Sebastian Meine
fonte