エラー666

概要

エラー666という滅多に見かけることがないエラーがあります。下記のようなメッセージのエラーとなります。

メッセージ 666、レベル 16、状態 2、行 1
パーティション ID が 72057594039828480 のインデックスで、重複するグループに対するシステム生成の一意値が最大値を超えました。この問題はインデックスを削除し、再作成することで解決できます。それ以外の場合は、別のクラスター化キーを使用してください。

目を引くエラー番号なのですが、どのような条件で発生するエラーなのか情報がないので、何故インデックスを再作成(再構築)することで解決するのか等々、よく判りませんでした。そこで、動きを確認して見ましたので、その結果をBlogで共有しておこうと思います。

なお、本検証結果はあくまでも検証ベースの結果であり、ソースコードの確認等で得られたものではありません。また、SQL Server 2008 R2 での確認になりますので、将来のバージョンでの動作については異なる可能性もあります。この点についはご理解いただいた上で、ご参照ください。

動作について

まず、メッセージにある 「重複するグループに対するシステム生成の一意値」 が何か?というところですが、これは下記の SQL Server 2008 R2 Books Online に説明があります。

クラスタ化インデックスの設計ガイドライン
UNIQUE プロパティを指定せずにクラスタ化インデックスが作成された場合、データベース エンジンにより、4 バイトの uniqueifier 列が自動的にテーブルに追加されます。必要があれば、各キーを一意にするため、データベース エンジンにより自動的に uniqueifier 値が行に追加されます。この列とその値は、内部的に使用されるもので、ユーザーが参照したりアクセスすることはできません。

平たく書くと、「一意でないクラスタ化インデックスでは、内部的に4バイトの列が追加されて、この値を一意性を保つのに使います」ということです。エラー666は、この値が最大値を超えてしまったために発生するということですね。

後は、この内部的な4バイトの列がどのように動作するか確認すればOKということで、検証してみました。どのような検証を行ったかは後述しますが、結論を書くと以下のような動作になりました。

1) この値は重複するグループ毎に振られる値です
例えば、以下のような構成のインデックスの場合、c1とc2の値が重複するレコードが同じグループとしてまとめられます。c1=1、c2=2、c3=1,C4=N’A’とc1=1、c2=2、c3=2、c4=N’A’というレコードをINSERTするとグループにまとめられ、2件目のレコードでこの値は1になるようです。c1=1、c2=3、c3=1,c4=N’A’というレコードは、違うグループになります。

create table test666(c1 int,c2 int,c3 int,c4 nchar(10))
go
create clustered index idx_c1c2 on test666(c1,c2)
go

2) INSERT時に割り振られる値は、そのグループの現在の最大値+1です
例えば、1)の例のc1=1、c2=2のグループの値が100まで進んでいれば、次のINSERTされるc1=1、c2=2のグループのレコードは101になります。

ここで重要なのは、 「現在の最大値」 ということです。100まで一旦カウントアップしたところで、値が2のレコード以外が削除された後、同じグループにレコードがINSERTされた場合、値は3になります。

3) 最大値は、2147483646です
INSERTするタイミングで、現在の最大値+1が2147483647(4バイトの符号付INTの最大値)となった場合に、このエラー666を出力しているようです。よって、値が2147483646になったら、INSERTできなくなります。

対応方法について

メッセージに 「インデックスを削除し、再作成することで解決できます」 とあるのは、"動作について"の2)で説明した動作があるためとなります。現在の値が進みすぎているので、再作成(再構築)することで値を小さくすることが回避方法となります。古いレコードは削除されていると期待して、振り直せば値が小さくなると信じている対応方法です。

では、「2147483647レコード実際にあってINSERTできない状況になっていたら?」という話ですが、 「それ以外の場合は、別のクラスター化キーを使用してください」 というのが回避方法になります。クラスタ化キーの構成の見直しをお願いします。

検証方法

"検証方法"と書くほどの事ではないのですが、「インサイドMicrosoft SQL Server 2005」等で紹介されています DBCC PAGE という公開していないコマンドを使用して、地道に検証しました。私たちサポートチームも、内部の動作を確認するためには、こうしたコマンドに頼って検証をしています。