L'incrément d'identité saute dans la base de données SQL Server

126

Dans l'une de mes tables Feede la colonne "ReceiptNo" dans l'incrément d'identité de la base de données SQL Server 2012, l'incrémentation de l'identité de la base de données SQL Server 2012 a soudainement commencé à passer à 100 au lieu de 1 en fonction des deux choses suivantes.

  1. si c'est 1205446 ça saute à 1206306, si c'est 1206321, ça saute à 1207306 et si c'est 1207314, ça saute à 1208306. Ce que je veux vous faire remarquer c'est que les trois derniers chiffres restent constants c'est à dire 306 à chaque saut se produit comme indiqué dans l'image suivante.

  2. ce problème se produit lorsque je redémarre mon ordinateur

entrez la description de l'image ici

kashif
la source
Si vous ajoutez order by ReceiptNoà votre requête, ces enregistrements ne sont-ils vraiment pas là? Êtes-vous sûr qu'il n'y a pas d'erreur lors de l'insertion d'enregistrements? Si un enregistrement tente d'être inséré et échoue, l'identité s'incrémentera, même chose si les enregistrements sont supprimés. Si des enregistrements sont supprimés, le fichier ReceiptNon'est pas réinitialisé. Pouvez-vous publier la table de création pour la Feetable?
Taryn
4
La première question est: pourquoi est-ce important? il devrait s'agir d'un identifiant unique arbitraire
Andrew
1
Est-ce que cela fonctionne sur un serveur ou est-ce peut-être express sur un bureau? Vous vous demandez pourquoi il semble que le service soit redémarré si fréquemment?
Martin Smith
@bluefeet Je sais que lorsque l'erreur se produit, l'incrément d'identité a lieu. Je suis sûr à 100% qu'il n'y a pas d'erreurs. Je modifie ma question en ajoutant une table et la procédure stockée que j'utilise pour insérer les lignes.
kashif
@kashif - 99% sûr que ce n'est pas nécessaire. Les sauts d'exactement 1000 ( 1206306, 1207306, 1207806) signifie l'explication dans le Connect article Discussion applique presque certainement.
Martin Smith

Réponses:

158

Vous rencontrez ce problème en raison d'une amélioration des performances depuis SQL Server 2012.

Il utilise désormais par défaut une taille de cache de 1 000 lors de l'allocation de IDENTITYvaleurs pour une intcolonne et le redémarrage du service peut «perdre» les valeurs inutilisées (la taille du cache est de 10 000 pour bigint/ numeric).

Ceci est mentionné dans la documentation

SQL Server peut mettre en cache les valeurs d'identité pour des raisons de performances et certaines des valeurs attribuées peuvent être perdues lors d'une panne de base de données ou d'un redémarrage du serveur. Cela peut entraîner des lacunes dans la valeur d'identité lors de l'insertion. Si les lacunes ne sont pas acceptables, l'application doit utiliser son propre mécanisme pour générer des valeurs clés. L'utilisation d'un générateur de séquence avec l' NOCACHEoption peut limiter les écarts aux transactions qui ne sont jamais validées.

D'après les données que vous avez affichées, il semble que cela se soit produit après la saisie des données du 22 décembre, puis au redémarrage, SQL Server a réservé les valeurs 1206306 - 1207305. Après la saisie des données du 24 au 25 décembre, un autre redémarrage a été effectué et SQL Server a réservé la plage suivante 1207306 - 1208305visible dans les entrées du 28.

À moins que vous ne redémarriez le service avec une fréquence inhabituelle, il est peu probable que les valeurs «perdues» entrainent un impact significatif dans la plage de valeurs autorisées par le type de données. La meilleure politique est donc de ne pas s'en soucier.

Si c'est pour une raison quelconque un réel problème pour vous, certaines solutions de contournement possibles sont ...

  1. Vous pouvez utiliser une à la SEQUENCEplace d'une colonne d'identité et définir une taille de cache plus petite par exemple et l'utiliser NEXT VALUE FORdans une colonne par défaut.
  2. Ou appliquez l'indicateur de trace 272 qui rend l' IDENTITYallocation journalisée comme dans les versions jusqu'à 2008 R2. Cela s'applique globalement à toutes les bases de données.
  3. Ou, pour les versions récentes, exécutez ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFFpour désactiver la mise en cache d'identité pour une base de données spécifique.

Vous devez être conscient qu'aucune de ces solutions de contournement n'assure aucune lacune. Cela n'a jamais été garanti IDENTITYcar cela ne serait possible qu'en sérialisant les insertions dans la table. Si vous avez besoin d'une colonne sans interruption, vous devrez utiliser une solution différente de celle IDENTITYouSEQUENCE

Martin Smith
la source
1
pour vérifier ce que vous avez dit, j'ai inséré des valeurs et obtenu 1208309, 1208310, puis j'ai redémarré le serveur, puis lorsque j'ai ajouté la ligne, j'ai obtenu 1209309 cela signifie que ce que vous avez dit est tout à fait exact. Merci beaucoup. maintenant pouvez-vous me dire comment puis-je résoudre ce problème. me suggéreriez-vous d'utiliser le serveur SQL 2008 au lieu de 2012 que j'utilisais auparavant ou même d'utiliser 2012 ce problème peut être résolu ??
kashif
1
@kashif - Est-ce vraiment un problème pour vous? Même si vous utilisez jusqu'à 1 000 valeurs d'identité par jour, il vous faudra encore 2 millions de jours avant de manquer de valeurs. Si vous voulez l'ancien comportement, vous pouvez définir SQL Server pour qu'il démarre avec l'indicateur de trace 272 ou vous pouvez utiliser un SEQUENCEau lieu d'un IDENTITYet définir la séquence pour avoir une taille de cache de 0.
Martin Smith
J'ai eu des réponses assez impressionnantes et satisfaisantes de votre part pour ma solution merci beaucoup.
kashif
En fait, d'après vous, CREATE TABLEje vois que vous utilisez numeric(7)et que vous avez commencé la numérotation à 1200001ce moment-là, cela signifie que vous vous épuiseriez après des 8,799jours (24 ans) si vous en utilisez 1000 par jour.
Martin Smith
La valeur réelle "sautée" est censée dépendre du type de colonne utilisé. Par exemple, une big intcolonne «sautera» généralement de 10 000 par redémarrage.
StarPilot
60

Ce problème se produit après le redémarrage de SQL Server.

La solution est:

  • Exécutez le Gestionnaire de configuration SQL Server .

  • Sélectionnez Services SQL Server .

    Gestionnaire de configuration SQL Server

  • Cliquez avec le bouton droit sur SQL Server et sélectionnez Propriétés .

  • Dans la fenêtre d'ouverture sous Paramètres de démarrage , tapez -T272et cliquez sur Ajouter , puis appuyez sur le bouton Appliquer et redémarrez.

    Paramètres de démarrage de SQL Server

Harun ERGUL
la source
1
Cette méthode fonctionne vraiment, merci beaucoup! et comme indiqué ici , ce problème ne sera pas résolu dans SQL Server 2012, et dans ses Service Packs, - uniquement dans la prochaine version.
Fragment
2
Existe-t-il un moyen d'appliquer l'indicateur de trace à des bases de données individuelles? Je ne veux pas effectuer ce changement sur l'ensemble du serveur car j'ai des bases de données tierces et je ne sais pas comment cela les affectera.
Ege Ersoz
1
Je n'ai pas suivi les raisons, mais apparemment, certains utilisateurs ont dû utiliser des «t» minuscules pour que cela fonctionne. Voir le lien publié par Fragment dans le commentaire ci-dessus.
Savage
33

À partir de, SQL Server 2017+vous pouvez utiliser ALTER DATABASE SCOPED CONFIGURATION :

IDENTITY_CACHE = {ON | OFF}

Active ou désactive le cache d'identité au niveau de la base de données. La valeur par défaut est ON. La mise en cache d'identité est utilisée pour améliorer les performances INSERT sur les tables avec des colonnes d'identité.Pour éviter les lacunes dans les valeurs de la colonne Identité dans les cas où le serveur redémarre de manière inattendue ou bascule vers un serveur secondaire, désactivez l'option IDENTITY_CACHE. Cette option est similaire à l'indicateur de trace SQL Server 272 existant, sauf qu'elle peut être définie au niveau de la base de données plutôt qu'au niveau du serveur uniquement.

(...)

G. Définir IDENTITY_CACHE

Cet exemple désactive le cache d'identité.

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

Je sais que ma réponse est peut-être en retard à la fête. Mais j'ai résolu d'une autre manière en ajoutant une procédure stockée de démarrage dans SQL Server 2012.

Créez une procédure stockée suivante dans la base de données principale.

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

Ensuite, ajoutez-le à Démarrer en utilisant la syntaxe suivante.

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

C'est une bonne idée si vous avez peu de tables. mais si vous devez le faire pour de nombreuses tables, cette méthode fonctionne toujours mais ce n'est pas une bonne idée.

Jeyara
la source
Bonne idée. Mais cela ne fonctionne pas pour les tables dépendantes, n'est-ce pas? Je veux dire, corrige-t-il les valeurs de clé étrangère?
rom5jp
@ rom5jp Fixing FK n'est pas le but de cette réponse. Il s'agit de fixer la prochaine valeur PK possible d'une table. Tant que MAX (id) n'est dans aucun des FK, cela devrait fonctionner.
Jeyara
14

C'est toujours un problème très courant chez de nombreux développeurs et applications, quelle que soit leur taille.

Malheureusement, les suggestions ci-dessus ne résolvent pas tous les scénarios, c'est-à-dire l'hébergement partagé, vous ne pouvez pas compter sur votre hôte pour définir le paramètre de démarrage -t272.

En outre, si vous avez des tables existantes qui utilisent ces colonnes d'identité pour les clés primaires, supprimer ces colonnes et en recréer de nouvelles pour utiliser la solution de contournement de séquence BS est un effort ÉNORME. La solution de contournement de séquence n'est bonne que si vous concevez les tables à partir de zéro dans SQL 2012+

En bout de ligne, si vous êtes sur Sql Server 2008R2, restez sur lui. Sérieusement, restez dessus. Jusqu'à ce que Microsoft admette qu'il a introduit un bogue ÉNORME, qui est toujours là même dans Sql Server 2016, nous ne devrions pas mettre à niveau jusqu'à ce qu'ils le possèdent et FIX IT.

Microsoft a tout de suite introduit un changement radical, c'est-à-dire qu'ils ont cassé une API fonctionnelle qui ne fonctionne plus comme prévu, du fait que leur système oublie leur identité actuelle lors d'un redémarrage. Cache ou pas de cache, c'est inacceptable, et le développeur Microsoft du nom de Bryan doit le posséder, au lieu de dire au monde qu'il est "par conception" et "fonctionnalité". Bien sûr, la mise en cache est une fonctionnalité, mais perdre la trace de ce que devrait être la prochaine identité, N'EST PAS UNE FONCTION. C'est un BUG fricken !!!

Je vais partager la solution de contournement que j'ai utilisée, car mes bases de données sont sur des serveurs d'hébergement partagé, et je ne supprime pas et ne recrée pas mes colonnes de clé primaire, ce serait un énorme PITA.

Au lieu de cela, c'est mon hack honteux (mais pas aussi honteux que ce bogue de point de vente que Microsoft a introduit).

Hack / Fix:

Avant vos commandes d'insertion, réalisez simplement votre identité avant chaque insertion. Ce correctif n'est recommandé que si vous n'avez pas de contrôle administrateur sur votre instance Sql Server, sinon je suggère de réensemencer au redémarrage du serveur.

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)

Juste ces 3 lignes juste avant votre insertion, et vous devriez être prêt à partir. Cela n'affectera pas vraiment les performances, c'est-à-dire qu'il sera imperceptible.

Bonne chance.

green_mystic_Orb
la source
7

Il existe de nombreuses raisons possibles pour sauter les valeurs identitaires. Ils vont des insertions restaurées à la gestion des identités pour la réplication. Qu'est-ce qui cause cela dans votre cas, je ne peux pas le dire sans passer du temps dans votre système.

Sachez cependant qu'en aucun cas vous ne pouvez supposer qu'une colonne d'identité est contiguos. Il y a tout simplement trop de choses qui peuvent causer des lacunes.

Vous pouvez trouver un peu plus d'informations à ce sujet ici: http://sqlity.net/en/792/the-gap-in-the-identity-value-sequence/

Sébastien Meine
la source