IDの増分がSQL Serverデータベースでジャンプしている


126

FeeSQL Server 2012データベースの列「ReceiptNo」にある私のテーブルの1つで、IDの増分が突然、次の2つに応じて1ではなく100にジャンプし始めました。

  1. 1205446の場合は1206306にジャンプし、1206321の場合は1207306にジャンプし、1207314の場合は1208306にジャンプします。次の図に示すように発生します。

  2. この問題は、コンピュータを再起動したときに発生します

ここに画像の説明を入力してください


order by ReceiptNoクエリに追加すると、それらのレコードは実際には存在しませんか?レコードが挿入されているときにエラーは発生しませんか?レコードが挿入されようとして失敗した場合、IDは増加します。レコードが削除された場合も同様です。レコードが削除されてReceiptNoもリセットされません。テーブルの作成テーブルを投稿できますFeeか?
タリン

4
最初の質問は-なぜそれが重要なのですか?任意の一意のIDである必要があります
Andrew

1
これはサーバーで実行されていますか、それともデスクトップで表示されますか?サービスがそれほど頻繁に再起動されているように思われるのはなぜですか?
Martin Smith

@bluefeetエラーが発生すると、IDの増分が行われます。私は100%エラーがないと確信しています。行を挿入するために使用するテーブルとストアドプロシージャを追加して、質問を編集しています。
kashif 2013年

@kashif-99%必要ありません。(正確に1,000ジャンプ120630612073061207806)接続項目のスレッドでの説明がほぼ確実に適用されることを意味します。
マーティン・スミス

回答:


158

SQL Server 2012以降のパフォーマンスの向上により、この動作が発生しています。

現在IDENTITYは、int列に値を割り当て、サービスを再起動すると未使用の値が「失われる」場合、デフォルトで1,000 のキャッシュサイズが使用されます(bigint/ のキャッシュサイズは10,000ですnumeric)。

これはドキュメントに記載されています

SQL Serverはパフォーマンス上の理由でID値をキャッシュする場合があり、割り当てられた値の一部は、データベース障害またはサーバーの再起動中に失われる可能性があります。これにより、挿入時にID値にギャップが生じる可能性があります。ギャップが許容できない場合、アプリケーションは独自のメカニズムを使用してキー値を生成する必要があります。NOCACHEオプションでシーケンスジェネレータを使用すると、コミットされないトランザクションのギャップを制限できます。

これまでに示したデータから、12月22日のデータ入力後にSQL Serverが再起動したときに値が予約されたように見えます1206306 - 1207305。12月24〜25日のデータ入力が行われた後、別の再起動が行われ、SQL Server 1207306 - 1208305は28番目のエントリに表示される次の範囲を予約しました。

異常な頻度でサービスを再起動しない限り、「失われた」値がデータ型で許可されている値の範囲に大きな凹みを作る可能性は低いので、最善のポリシーはそのことを心配しないことです。

これが何らかの理由であなたに本当の問題である場合、いくつかの可能な回避策は...

  1. SEQUENCEたとえば、ID列の代わりにを使用して、より小さなキャッシュサイズを定義NEXT VALUE FORし、列のデフォルトで使用できます。
  2. またはIDENTITY、2008 R2までのバージョンのように割り当てをログに記録するトレースフラグ272を適用します。これはすべてのデータベースにグローバルに適用されます。
  3. または、最近のバージョンでは、実行ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFFして特定のデータベースのIDキャッシュを無効にします。

これらの回避策はどれもギャップがないことを保証していないことに注意してください。これはIDENTITY、テーブルへの挿入をシリアル化することによってのみ可能になるため、保証されていません。ギャップのないカラムが必要な場合は、IDENTITYまたはどちらかとは異なるソリューションを使用する必要がありますSEQUENCE


1
あなたが言ったことを確認するために、いくつかの値を挿入して1208309、1208310を取得し、次にサーバーを再起動しました。その後、行を追加すると、1209309を取得しました。どうもありがとう。どうすればこの問題を解決できるか教えてください。以前使用していた2012の代わりにSQL Server 2008を使用することをお勧めしますか、2012を使用している場合でも、この問題は解決できますか?
kashif 2013年

1
@kashif-それは実際にあなたにとって問題ですか?1日に最大1,000個のID値を使用しても、値がなくなるまでに200万日かかります。古い動作が必要な場合は、SQL Serverをトレースフラグ272で起動するように設定するか、のSEQUENCE代わりにを使用IDENTITYして、シーケンスをキャッシュサイズに設定することができます0
マーティン・スミス

私のソリューションのおかげで、あなたからかなり印象的で満足のいく答えが得られました。
kashif 2013年

実際、あなたCREATE TABLEから見て、あなたが使用numeric(7)していることを確認し、1日に1,000を使用1200001すると、8,799数日(24年)後に使い果たされることになります。
Martin Smith

「ジャンプ」された実際の値は、使用される列のタイプに依存すると想定されています。たとえば、big int列は通常、再起動ごとに10,000ずつ「ジャンプ」します。
StarPilot 2014年

60

この問題は、SQL Serverを再起動した後に発生します。

解決策は次のとおりです。

  • SQL Server構成マネージャーを実行します

  • [ SQL Serverサービス]を選択します

    SQL Server構成マネージャー

  • [ SQL Server ]を右クリックし、[ プロパティ]を選択します

  • 下の開口窓で起動パラメータ、タイプ-T272をクリックして[追加]を、押し適用ボタンと再起動を。

    SQL Serverの起動パラメーター


1
この方法は本当に効果的です。ここで述べたように、この問題はSQL Server 2012およびそのサービスパックでは修正されません-次のバージョンのリリースでのみ修正されます。
2015

2
トレースフラグを個々のデータベースに適用する方法はありますか?サードパーティのデータベースがあり、これがどのようにそれらに影響するかは不明なので、サーバー全体でこの変更を行いたくありません。
Ege Ersoz、2015

1
理由には従いませんでしたが、一部のユーザーは小文字の "t"を使用して機能させる必要があったようです。上記のコメントでFragmentによって投稿されたリンクを参照してください。
サベージ

33

SQL Server 2017+あなたは使用することができますALTER DATABASEのスコープの設定を

IDENTITY_CACHE = {ON | オフ}

データベースレベルでIDキャッシュを有効または無効にします。デフォルトはオンです。IDキャッシングは、ID列を持つテーブルでのINSERTパフォーマンスを向上させるために使用されます。サーバーが予期せず再起動したり、セカンダリサーバーにフェールオーバーしたりする場合に、Identity列の値のギャップを回避するには、IDENTITY_CACHEオプションを無効にします。 このオプションは既存のSQL Serverトレースフラグ272と似ていますが、サーバーレベルだけでなくデータベースレベルでも設定できる点が異なります。

(...)

G. IDENTITY_CACHEを設定する

この例では、IDキャッシュを無効にします。

ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE=OFF ;

25

私の答えがパーティーに遅れる可能性があることを知っています。しかし、SQL Server 2012にスタートアップストアドプロシージャを追加することで、別の方法で解決しました。

マスターDBに次のストアドプロシージャを作成します。

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';

テーブルが少ない場合、これは良い考えです。しかし、多くのテーブルで行う必要がある場合でも、この方法は機能しますが、良い方法ではありません。


いいアイディア。しかし、従属テーブルでは機能しませんか?つまり、外部キーの値を修正しませんか?
rom5jp

@ rom5jp FKの修正はこの回答のポイントではありません。そのすべては、テーブルの可能な次のPK値を修正することに関するものです。MAX(id)がどのFKにもない限り、機能します。
ジェヤラ

14

これは、サイズに関係なく、多くの開発者やアプリケーションで依然として非常に一般的な問題です。

残念ながら、上記の提案はすべてのシナリオ、つまり共有ホスティングを修正するわけではありません。ホストに依存して-t272起動パラメーターを設定することはできません。

また、これらのID列を主キーに使用する既存のテーブルがある場合、それらの列を削除し、新しい列を再作成してBSシーケンスの回避策を使用するのは非常に困難です。シーケンスの回避策は、SQL 2012+で最初からテーブルを新しく設計する場合にのみ有効です。

結論としては、SQL Server 2008R2を使用している場合はそのままにしておきます。真剣に、それにとどまります。Sql Server 2016にも存在する巨大なバグが導入されたことをマイクロソフトが認めるまでは、Microsoftが所有して修正するまでアップグレードしないでください。

Microsoftはまっすぐに重大な変更を導入しました。つまり、システムが再起動時に現在のIDを忘れてしまうため、設計どおりに機能しないAPIが機能しなくなりました。キャッシュまたはキャッシュなし。これは受け入れられません。ブライアンという名前のMicrosoft開発者は、「仕様による」「機能」であることを世界に知らせる代わりに、それを所有する必要があります。確かに、キャッシングは機能ですが、次のIDが何であるかを追跡することは機能ではありません。とんでもないバグです!!!

私のDBは共有ホスティングサーバー上にあるため、使用した回避策を共有します。また、主キー列を削除して再作成しないため、巨大なPITAになります。

代わりに、これは私の恥ずべきハックです(ただし、マイクロソフトが導入したこのPOSバグほど恥ずべきではありません)。

ハック/修正:

挿入コマンドの前に、各挿入の前にIDを再シードします。この修正は、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行だけで十分です。パフォーマンスにはそれほど影響しません。つまり、目立たなくなります。

幸運を。


7

ID値をジャンプする理由は多数考えられます。それらは、ロールバックされた挿入からレプリケーションのID管理にまで及びます。あなたの場合にこれを引き起こしているのは、システムで少し時間を費やすことなしにはわかりません。

ただし、ID列が連続していると想定することはできません。ギャップを引き起こす可能性のあるものが多すぎます。

これに関するもう少し詳しい情報はここにあります:http : //sqlity.net/en/792/the-gap-in-the-identity-value-sequence/

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.