L'incremento di identità sta saltando nel database di SQL Server


126

In una delle mie tabelle Feenella colonna "ReceiptNo" in SQL Server 2012 l'incremento dell'identità del database ha iniziato improvvisamente a saltare a 100 invece di 1 a seconda delle due cose seguenti.

  1. se è 1205446 salta a 1206306, se è 1206321 salta a 1207306 e se è 1207314 salta a 1208306. Quello che voglio farti notare è che le ultime tre cifre rimangono costanti cioè 306 ogni volta che il salto si verifica come mostrato nella figura seguente.

  2. questo problema si verifica quando riavvio il computer

inserisci qui la descrizione dell'immagine


Se aggiungi order by ReceiptNoalla tua query, quei record non sono davvero lì? Sei sicuro che quando vengono inseriti i record non ci siano errori? Se un record tenta di essere inserito e fallisce, l'identità aumenterà, stessa cosa se i record vengono eliminati. Se i record vengono eliminati, il file ReceiptNonon viene ripristinato. Puoi pubblicare la tabella di creazione per la Feetabella?
Taryn

4
La prima domanda è: perché è importante? dovrebbe essere un ID univoco arbitrario
Andrew

1
Funziona su un server o è forse espresso su un desktop? Ti chiedi perché sembra che il servizio venga riavviato così spesso?
Martin Smith

@bluefeet So che quando si verifica l'errore, si verifica l'incremento dell'identità. Sono sicuro al 100% che non ci siano errori. Sto modificando la mia domanda aggiungendo la tabella e la procedura memorizzata che utilizzo per inserire le righe.
kashif

@kashif - 99% sicuro che non sia necessario. I salti da esattamente 1.000 ( 1206306, 1207306, 1207806) si intende la spiegazione nel collegamento Discussione Articolo vale quasi certamente.
Martin Smith

Risposte:


158

Si riscontra questo comportamento a causa di un miglioramento delle prestazioni rispetto a SQL Server 2012.

Ora, per impostazione predefinita, utilizza una dimensione della cache di 1.000 quando si allocano i IDENTITYvalori per una intcolonna e il riavvio del servizio può "perdere" i valori inutilizzati (la dimensione della cache è 10.000 per bigint/ numeric).

Questo è menzionato nella documentazione

SQL Server potrebbe memorizzare nella cache i valori di identità per motivi di prestazioni e alcuni dei valori assegnati potrebbero andare persi durante un errore del database o il riavvio del server. Ciò può provocare lacune nel valore di identità al momento dell'inserimento. Se le lacune non sono accettabili, l'applicazione deve utilizzare il proprio meccanismo per generare i valori chiave. L'utilizzo di un generatore di sequenze con l' NOCACHEopzione può limitare gli spazi vuoti alle transazioni che non vengono mai salvate.

Dai dati che hai mostrato sembra che questo sia successo dopo l'immissione dei dati per il 22 dicembre, quindi quando è stato riavviato SQL Server ha riservato i valori 1206306 - 1207305. Dopo l'immissione dei dati per il 24-25 dicembre è stato eseguito un altro riavvio e SQL Server ha riservato il successivo intervallo 1207306 - 1208305visibile nelle voci per il 28.

A meno che non si riavvii il servizio con una frequenza insolita, è improbabile che i valori "persi" incidano in modo significativo nell'intervallo di valori consentito dal tipo di dati, quindi la migliore politica è non preoccuparsene.

Se questo è per qualche motivo un vero problema per te, alcune possibili soluzioni alternative sono ...

  1. Puoi usare un file SEQUENCE invece di una colonna Identity e definire una dimensione della cache inferiore, ad esempio, e utilizzarla NEXT VALUE FORin una colonna predefinita.
  2. Oppure applica il flag di traccia 272 che rende l' IDENTITYallocazione registrata come nelle versioni fino alla 2008 R2. Questo vale globalmente per tutti i database.
  3. Oppure, per le versioni recenti, eseguire ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFFper disabilitare la memorizzazione nella cache delle identità per un database specifico.

È necessario essere consapevoli che nessuna di queste soluzioni garantisce l'assenza di lacune. Ciò non è mai stato garantito IDENTITYin quanto sarebbe possibile solo serializzando gli inserti nella tabella. Se hai bisogno di una colonna senza spazi, dovrai usare una soluzione diversa da IDENTITYoSEQUENCE


1
per verificare quello che hai detto ho inserito dei valori e ottenuto 1208309, 1208310 e poi ho riavviato il server e poi quando ho aggiunto la riga ho ottenuto 1209309 vuol dire che quello che hai detto è assolutamente corretto. molte grazie. ora puoi per favore dirmi come posso risolvere questo problema. mi consiglieresti di utilizzare sql server 2008 invece del 2012 che stavo utilizzando in precedenza o anche utilizzando 2012 questo problema può essere risolto ??
kashif

1
@kashif - È davvero un problema per te? Anche se utilizzi fino a 1.000 valori di identità al giorno, ci vorranno comunque 2 milioni di giorni prima di esaurire i valori. Se si desidera il vecchio comportamento, è possibile impostare SQL Server all'avvio con il flag di traccia 272 oppure è possibile utilizzare a SEQUENCEinvece di IDENTITYe impostare la sequenza in modo che abbia una dimensione della cache di 0.
Martin Smith

Ho ricevuto risposte abbastanza impressionanti e soddisfacenti da te per la mia soluzione, grazie mille.
kashif

In realtà dal tuo CREATE TABLEVedo che stai usando numeric(7)e hai iniziato la numerazione a 1200001questo significa che finiresti dopo 8,799giorni (24 anni) se ne usi 1.000 al giorno.
Martin Smith

Si suppone che il valore effettivo "saltato" dipenda dal tipo di colonna utilizzato. Ad esempio, una big intcolonna normalmente "salta" di 10.000 per riavvio.
StarPilot

60

Questo problema si verifica dopo il riavvio di SQL Server.

La soluzione è:

  • Esegui Gestione configurazione SQL Server .

  • Seleziona Servizi SQL Server .

    Gestione configurazione SQL Server

  • Fare clic con il pulsante destro del mouse su SQL Server e selezionare Proprietà .

  • Nella finestra di apertura in Parametri di avvio , digitare -T272e fare clic su Aggiungi , quindi premere il pulsante Applica e riavviare.

    Parametri di avvio di SQL Server


1
Questo metodo funziona davvero, grazie mille! e come detto qui , questo problema non verrà risolto in SQL Server 2012 e nei suoi service pack, solo nella prossima versione.
Frammento

2
C'è un modo per applicare il flag di traccia ai singoli database? Non voglio apportare questa modifica all'intero server perché ho database di terze parti e non sono sicuro di come ciò li influenzerà.
Ege Ersoz

1
Non ho seguito i motivi, ma a quanto pare alcuni utenti hanno dovuto usare la "t" minuscola per farlo funzionare. Vedi il link pubblicato da Fragment nel commento sopra.
Savage

33

A partire dal SQL Server 2017+ è possibile utilizzare ALTER DATABASE SCOPED CONFIGURATION :

IDENTITY_CACHE = {ON | OFF}

Abilita o disabilita la cache delle identità a livello di database. L'impostazione predefinita è ON. La memorizzazione nella cache delle identità viene utilizzata per migliorare le prestazioni di INSERT nelle tabelle con colonne Identity. Per evitare lacune nei valori della colonna Identità nei casi in cui il server si riavvia in modo imprevisto o esegue il failover su un server secondario, disabilitare l'opzione IDENTITY_CACHE. Questa opzione è simile all'esistente flag di traccia di SQL Server 272, tranne per il fatto che può essere impostata a livello di database anziché solo a livello di server.

(...)

G. Imposta IDENTITY_CACHE

Questo esempio disabilita la cache delle identità.

ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE=OFF ;

25

So che la mia risposta potrebbe essere in ritardo alla festa. Ma ho risolto in un altro modo aggiungendo una stored procedure di avvio in SQL Server 2012.

Creare una procedura memorizzata seguente nel database master.

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

Quindi aggiungilo all'avvio utilizzando la seguente sintassi.

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

Questa è una buona idea se hai pochi tavoli. ma se devi farlo per molte tabelle, questo metodo funziona ancora ma non è una buona idea.


Buona idea. Ma non funziona per le tabelle dipendenti, vero? Voglio dire, aggiusta i valori delle chiavi esterne?
rom5jp

@ rom5jp Fixing FK non è il punto di questa risposta. Si tratta di fissare il possibile valore PK successivo di una tabella. Finché MAX (id) non è in nessuno degli FK, dovrebbe funzionare.
Jeyara

14

Questo è ancora un problema molto comune tra molti sviluppatori e applicazioni indipendentemente dalle dimensioni.

Sfortunatamente i suggerimenti di cui sopra non risolvono tutti gli scenari, ad esempio hosting condiviso, non puoi fare affidamento sul tuo host per impostare il parametro di avvio -t272.

Inoltre, se disponi di tabelle esistenti che utilizzano queste colonne di identità per le chiavi primarie, è uno sforzo ENORME eliminare quelle colonne e ricrearne di nuove per utilizzare la soluzione alternativa alla sequenza BS. La soluzione alternativa per la sequenza è utile solo se si progettano nuove tabelle da zero in SQL 2012+

La linea di fondo è che, se sei su Sql Server 2008R2, RIMANI SU DI ESSO. Seriamente, rimani su. Fino a quando Microsoft non ammetterà di aver introdotto un bug ENORME, che è ancora presente anche in Sql Server 2016, non dovremmo eseguire l'aggiornamento fino a quando non lo possiedono e FIX IT.

Microsoft ha introdotto una modifica sostanziale, ovvero ha rotto un'API funzionante che non funziona più come progettato, a causa del fatto che il loro sistema dimentica la loro identità attuale al riavvio. Cache o nessuna cache, questo è inaccettabile e lo sviluppatore Microsoft con il nome di Bryan deve possederlo, invece di dire al mondo che è "by design" e una "funzionalità". Certo, il caching è una caratteristica, ma perdere traccia di quale dovrebbe essere la prossima identità, NON È UNA CARATTERISTICA. È un BUG impazzito !!!

Condividerò la soluzione alternativa che ho usato, perché i miei DB sono su server di hosting condiviso, inoltre, non sto rilasciando e ricreando le mie colonne chiave primaria, sarebbe un enorme PITA.

Invece, questo è il mio vergognoso trucco (ma non così vergognoso come questo bug POS introdotto da Microsoft).

Hack / Fix:

Prima dei comandi di inserimento, è sufficiente eseguire il seeding della propria identità prima di ogni inserimento. Questa correzione è consigliata solo se non si dispone del controllo amministrativo sull'istanza di Sql Server, altrimenti suggerisco di eseguire il reseeding al riavvio del 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)

Solo quelle 3 righe immediatamente prima del tuo inserto, e dovresti essere a posto. Non influirà molto sulle prestazioni, ovvero sarà impercettibile.

In bocca al lupo.


7

Ci sono molte possibili ragioni per saltare i valori di identità. Si va dagli inserti con rollback alla gestione delle identità per la replica. Cosa sta causando questo nel tuo caso non posso dirlo senza passare un po 'di tempo nel tuo sistema.

Tuttavia, dovresti sapere che in nessun caso puoi assumere che una colonna Identity sia contigua. Ci sono troppe cose che possono causare lacune.

Puoi trovare qualche informazione in più su questo qui: http://sqlity.net/en/792/the-gap-in-the-identity-value-sequence/

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.