在 80M 行索引表上运行插入查询时出现 MSG 666

Igo*_*lin 10 index sql-server-2008 sql-server

奇怪的是,我的存储过程开始收到一些输入数据的消息 666。

存储过程在最后一步尝试将行插入具有以下结构的表时失败:

Columns:
A_Id: PK, int
B_Id: PK, FK, int
C_Id: PK, FK, int
D_Id: PK, smallint 
Run Code Online (Sandbox Code Playgroud)

这本质上是一个将所有引用实体连接在一起的表。

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)
Run Code Online (Sandbox Code Playgroud)

两个索引的碎片化程度都很低 (<25%)。然而 PK_TableName 碎片迅速增长,因为对表的操作量非常大。

桌子尺寸:

Row count: ~80,000,000 rows
Run Code Online (Sandbox Code Playgroud)

因此,当我尝试运行一个简单的查询时,对于某些 D_Id,我收到以下消息:

消息 666。对于分区 ID 为 422223771074560 的索引,已超出系统为重复组生成的最大唯一值。删除并重新创建索引可能会解决此问题;否则,使用另一个聚类键。

查询示例:

INSERT INTO TableName
(A_Id,B_Id,C_Id,D_id)
VALUES (1,1,1,14)
Run Code Online (Sandbox Code Playgroud)

例如,当我将 D_Id 设置为某些值时 - 它失败,例如“14”。如果我将 D_ID 设置为其他值 (1,2,3,...13, 15,16,...),则查询运行良好。

我怀疑索引有一些非常糟糕的事情......但我无法深入了解...... :(为什么它会失败?

Mar*_*ith 16

Remus 提到的低选择性问题本身不足以导致该大小表上的问题。

唯一标识符开始于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)
Run Code Online (Sandbox Code Playgroud)

+---+---+-------------+
| X | Y | Uniqueifier |
+---+---+-------------+
| 1 | 1 |             |
| 1 | 2 |           1 |
| 1 | 3 |           2 |
| 2 | 4 |             |
| 2 | 5 |           1 |
+---+---+-------------+
Run Code Online (Sandbox Code Playgroud)

然后运行

DELETE FROM T 
WHERE Y IN (2,3)

INSERT INTO T VALUES (1),(1)
Run Code Online (Sandbox Code Playgroud)

+---+---+-------------+
| X | Y | Uniqueifier |
+---+---+-------------+
| 1 | 1 |             |
| 1 | 6 |           3 |
| 1 | 7 |           4 |
| 2 | 4 |             |
| 2 | 5 |           1 |
+---+---+-------------+
Run Code Online (Sandbox Code Playgroud)

在这种情况下显示唯一标识符没有重用已删除行中的值。

但是然后运行

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

+---+---+-------------+
| X | Y | Uniqueifier |
+---+---+-------------+
| 1 | 1 |             |
| 1 | 8 |           1 |
| 1 | 9 |           2 |
| 2 | 4 |             |
| 2 | 5 |           1 |
+---+---+-------------+
Run Code Online (Sandbox Code Playgroud)

显示删除具有最高唯一值的重复项后可以重置高水位标记。延迟是为了允许幽灵记录清理过程运行。

因为寿命太短,无法插入 20 亿个重复项,然后我使用DBCC WRITEPAGE手动将最高值调整uniqueifier为 2,147,483,644

在此处输入图片说明

然后我跑了

INSERT INTO T VALUES (1)
Run Code Online (Sandbox Code Playgroud)

多次。它成功了两次,第三次尝试失败,出现错误 666。

这实际上比我想象的要低。这意味着插入的最高唯一标识符是 2,147,483,646 而不是最大的 int 大小 2,147,483,647