80M行のインデックス付きテーブルで挿入クエリを実行するとMSG 666


10

奇妙なことに、私のストアード・プロシージャーは、一部の入力データについてメッセージ666を受け取り始めました。

次の構造を持つテーブルに行を挿入しようとすると、最後のステップでストアドプロシージャが失敗します。

Columns:
A_Id: PK, int
B_Id: PK, FK, int
C_Id: PK, FK, int
D_Id: PK, smallint 

これは基本的に、参照されるすべてのエンティティを接続するテーブルです。

Indexes:
IX_TableName_D_id - Clustered index on D_id column
PK_TableName - Unique non-clustered index on all columns (A_Id, B_Id, C_Id, D_Id)

両方のインデックスの断片化は低い(25%未満)。ただし、テーブルに対する操作の量が非常に多いため、PK_TableNameの断片化は急速に増大します。

テーブルサイズ:

Row count: ~80,000,000 rows

したがって、veeery simpleクエリを実行しようとすると、一部のD_Idに対して次のメッセージが表示されます。

メッセージ666。パーティションID 422223771074560のインデックスで、重複グループのシステム生成の一意の最大値を超えました。インデックスを削除して再作成すると、これが解決する場合があります。それ以外の場合は、別のクラスタリングキーを使用します。

クエリ例:

INSERT INTO TableName
(A_Id,B_Id,C_Id,D_id)
VALUES (1,1,1,14)

たとえば、D_Idをいくつかの値に設定すると失敗します。たとえば、「14」です。D_IDを他の値(1,2,3、... 13、15,16、...)に設定すると、クエリは正常に実行されます。

インデックスで本当に悪いことが起こっているのではないかと思います...しかし、私はこれの底に着くことができません... :(なぜ失敗するのですか?

回答:


16

Remusによって言及された低い選択性の問題だけでは、そのサイズテーブルで問題を引き起こすには不十分です。

uniqueifierはで始まり、実際に範囲をオーバーフロー1する2,147,483,646前に上がることがあります。

また、問題を確認するには、削除と挿入を繰り返す正しいパターンが必要です。

CREATE TABLE T
(
X SMALLINT,
Y INT IDENTITY PRIMARY KEY NONCLUSTERED
)

CREATE CLUSTERED INDEX IX ON T(X)

INSERT INTO T VALUES (1),(1),(1),(2),(2)

与える

+---+---+-------------+
| X | Y | Uniqueifier |
+---+---+-------------+
| 1 | 1 |             |
| 1 | 2 |           1 |
| 1 | 3 |           2 |
| 2 | 4 |             |
| 2 | 5 |           1 |
+---+---+-------------+

その後、実行

DELETE FROM T 
WHERE Y IN (2,3)

INSERT INTO T VALUES (1),(1)

与える

+---+---+-------------+
| X | Y | Uniqueifier |
+---+---+-------------+
| 1 | 1 |             |
| 1 | 6 |           3 |
| 1 | 7 |           4 |
| 2 | 4 |             |
| 2 | 5 |           1 |
+---+---+-------------+

その場合、一意名は削除された行の値を再利用しませんでした。

ただし、実行中

DELETE FROM T 
WHERE Y IN (6,7)
WAITFOR DELAY '00:00:10'
INSERT INTO T VALUES (1),(1)

与える

+---+---+-------------+
| X | Y | Uniqueifier |
+---+---+-------------+
| 1 | 1 |             |
| 1 | 8 |           1 |
| 1 | 9 |           2 |
| 2 | 4 |             |
| 2 | 5 |           1 |
+---+---+-------------+

最も高い一意識別子の値を持つ重複を削除した後、最高水準点をリセットできることを示します。遅延は、ゴーストレコードのクリーンアッププロセスを実行できるようにするためでした。

20億の複製を挿入するには寿命が短すぎるのでDBCC WRITEPAGE、手動で最高値uniqueifierを2,147,483,644に調整しました

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

次に走った

INSERT INTO T VALUES (1)

複数回。2回成功し、3回目の試行でエラー666で失敗しました。

これは実際には、私が想定したよりも1つ低かった。挿入された最も高い一意名が2,147,483,647の最大intサイズではなく2,147,483,646であることを意味します


情報提供の目的でTRUNCATE TABLE、一意識別子がリセットされるかどうかを確認できますか?
Jon Seigel 2013

@JonSeigel-はい、そうです。実行INSERT INTO T VALUES (1),(1),(1),(2),(2);TRUNCATE TABLE T;INSERT INTO T VALUES (1),(1),(1),(2),(2)後、最も高い一意識別子は2 、そのキー(ゴーストレコードを含む)に既に存在する最も高い一意識別子を調べると思います
Martin Smith
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.