Sql server 2005 我应该在此SQL 2005表上创建唯一聚集索引还是非唯一聚集索引?
我有一张存储数百万行的表。它看起来像这样:Sql server 2005 我应该在此SQL 2005表上创建唯一聚集索引还是非唯一聚集索引?,sql-server-2005,indexing,Sql Server 2005,Indexing,我有一张存储数百万行的表。它看起来像这样: Table_Docs ID, Bigint (Identity col) OutputFileID, int Sequence, int …(many other fields) 我们发现自己的处境是,设计它的开发人员将OutputFileID作为聚集索引。这不是唯一的。可以有数千条具有此ID的记录。它对使用此表的任何进程都没有好处,因此我们计划将其删除 问题是,改成什么…我有两个候选人,身份栏是一个自然的选择。然而,我们有一个进程,它在这个表上执行
Table_Docs
ID, Bigint (Identity col)
OutputFileID, int
Sequence, int
…(many other fields)
我们发现自己的处境是,设计它的开发人员将OutputFileID作为聚集索引。这不是唯一的。可以有数千条具有此ID的记录。它对使用此表的任何进程都没有好处,因此我们计划将其删除
问题是,改成什么…我有两个候选人,身份栏是一个自然的选择。然而,我们有一个进程,它在这个表上执行许多更新命令,并且它使用序列来执行这些操作。序列是非唯一的。大多数记录只包含一个,但大约20%的记录可以有两个或更多具有相同序列的记录
INSERT应用程序是一个VB6 crud,在表中抛出数千个INSERT命令。插入的值从不按任何特定顺序排列。因此,一次插入的顺序可能是12345,下一次插入的顺序可能是12245。我知道这可能会导致SQL移动大量数据以保持聚集索引的有序。然而,插入件的顺序通常接近有序。所有插入都将在集群表的末尾进行。我有500万张唱片,序列从100万到500万。插入应用程序将在任何给定时间在该范围的末尾插入序列。数据的重新排序应该是最小的(最多数万条记录)
现在,更新应用程序是我们的.NET之星。它对序列列执行所有更新<代码>“更新表\u文档集Feild1=这个,字段2=那个…其中Sequence=12345”–每天数十万个这样的文件。更新是完全、完全、随机的,涉及到表中的所有点
所有其他进程都只是在此(网页)上进行选择。常规索引包括这些
所以我的问题是,什么更好呢?…ID列上的唯一聚集索引有益于插入应用程序,还是序列上的非唯一聚集索引有益于更新应用程序 首先,我肯定会推荐一个聚集索引 其次,您的聚集索引:
- 狭窄的
- 静态(从未或几乎从未改变)
- 独特的
- 日增
序列
,因此一定要在其上添加非聚集索引
请参阅Kimberly Tripp's Excellate,以获取有关该主题的重要背景信息。一般来说,您希望您的聚集索引是唯一的。如果不是,SQL Server实际上会向它添加一个隐藏的“uniquifier”,以强制它是唯一的,这会增加开销 因此,最好使用ID列作为索引
作为旁注,使用标识列作为主键通常被称为代理键,因为它不是数据中固有的。当您有一个独特的自然关键可用,这可能是一个更好的选择。在这种情况下,看起来您没有这样做,因此使用唯一代理键是有意义的。插入顺序混乱最糟糕的事情是页面分割 当
SQL Server
需要在现有索引页中插入新记录,但在其中找不到位置时,它会从该页中取出一半记录并将其移动到新的索引页中
比如说,整个页面都有这些记录:
1 2 3 4 5 6 7 8 9
并且需要插入一个10
。在这种情况下,SQL Server
将只启动新页面
但是,如果您有以下情况:
1 2 3 4 5 6 7 8 11
,10
应该在11
之前。在这种情况下,SQL Server
将记录从6
移动到11
到新页面:
6 7 8 9 10 11
可以很容易地看到,旧页面将保持半满状态(只有从1
到6
的记录才会进入该页面,这些记录非常重要)
这将增加索引大小
让我们创建两个示例表:
CREATE TABLE perfect (id INT NOT NULL PRIMARY KEY, stuffing VARCHAR(300))
CREATE TABLE almost_perfect (id INT NOT NULL PRIMARY KEY, stuffing VARCHAR(300))
;
WITH q(num) AS
(
SELECT 1
UNION ALL
SELECT num + 1
FROM q
WHERE num < 200000
)
INSERT
INTO perfect
SELECT num, REPLICATE('*', 300)
FROM q
OPTION (MAXRECURSION 0)
;
WITH q(num) AS
(
SELECT 1
UNION ALL
SELECT num + 1
FROM q
WHERE num < 200000
)
INSERT
INTO almost_perfect
SELECT num + CASE num % 5 WHEN 0 THEN 2 WHEN 1 THEN 0 ELSE 1 END, REPLICATE('*', 300)
FROM q
OPTION (MAXRECURSION 0)
EXEC sp_spaceused N'perfect'
EXEC sp_spaceused N'almost_perfect'
perfect 200000 66960 KB 66672 KB 264 KB 24 KB
almost_perfect 200000 128528 KB 128000 KB 496 KB 32 KB
CREATE TABLE perfect(id INT NOT NULL主键,填充VARCHAR(300))
创建几乎完美的表(id INT NOT NULL主键,填充VARCHAR(300))
;
q(num)为
(
选择1
联合所有
选择num+1
来自q
其中num<200000
)
插入
变得完美
选择num,REPLICATE('*',300)
来自q
选项(最大递归0)
;
q(num)为
(
选择1
联合所有
选择num+1
来自q
其中num<200000
)
插入
近乎完美
选择num+CASE num%5当0时,然后选择2当1时,然后选择0否则1结束,复制('*',300)
来自q
选项(最大递归0)
EXEC sp_spaceused N'perfect'
EXEC sp_spaceused N‘近乎完美’
完美200000 66960 KB 66672 KB 264 KB 24 KB
几乎完美200000 128528 KB 128000 KB 496 KB 32 KB
即使只有20%
记录出现无序的概率,表也会变大两倍
另一方面,在序列
上使用聚集键将减少I/O
两次(因为它可以通过单个聚集索引查找而不是两个未聚集索引查找来完成)
因此,我将从您的数据中抽取一个样本子集,将其插入到测试表中,并使用序列上的聚集索引
,然后测量结果表的大小
如果它小于索引位于ID
上的同一个表的两倍大小,我会选择聚集索引位于Sequence
(因为产生的I/O
的总数会更少)
如果你决定