我应该摆脱Guid列上的聚簇索引

时间:2008-11-10 11:06:48

标签: sql-server guid clustered-index

我正在开发一个通常使用GUID作为主键的数据库。

默认情况下,SQL Server会在主键列上放置聚簇索引。我理解这对于GUID列来说是一个愚蠢的想法,并且非聚集索引更好。

您如何看待 - 我是否应该删除所有聚簇索引并将其替换为非聚集索引?

为什么SQL的性能调优器不会将此作为推荐?

10 个答案:

答案 0 :(得分:27)

聚簇索引的一个重要原因是您经常要为给定列检索一系列值的行。由于数据按此顺序进行物理排列,因此可以非常有效地提取行。

像GUID这样的东西,虽然对于主键很好,但可能会对性能产生积极的影响,因为插入会产生额外的成本,并且在选择上没有明显的好处。

所以是的,不要在GUID上集群索引。

至于为什么它不作为推荐提供,我建议调谐器知道这个事实。

答案 1 :(得分:24)

您几乎肯定希望在数据库中的每个表上建立聚簇索引。 如果表没有聚集索引,则称为“堆”,大多数类型的常见查询的性能为less for a heap than for a clustered index table

应建立聚集索引的哪些字段取决于表本身,以及针对该表的查询的预期使用模式。在几乎每种情况下,您可能希望聚簇索引位于列或唯一的列组合上,即(备用键),因为如果不是,SQL将在任何结尾处添加唯一值无论如何你选择的字段。如果您的表中有一列或多列,查询将经常使用这些列来选择或过滤多个记录(例如,如果您的表包含销售交易,您的应用程序将经常按产品ID请求销售交易,甚至更好,一个发票详细信息表,几乎在每种情况下,您将检索特定发票的所有详细记录,或者您经常检索特定客户的所有发票的发票表...无论您选择哪个大都是如此单个值或一系列值记录的数量)

这些列是聚集索引的候选列。聚簇索引中列的顺序很重要。索引中定义的第一列应该是在预期查询中首先选择或筛选的列。

所有这一切的原因是基于理解数据库索引的内部结构。这些索引称为平衡树(B-Tree)索引。它们有点像二叉树,除了树中的每个节点都可以有任意数量的条目(和子节点),而不是只有两个。使聚簇索引不同的原因是聚簇索引中的叶节点是表本身的实际物理磁盘数据页。而非聚集索引的叶节点只是“指向”表的数据页。

因此,当表具有clsutered索引时,表数据页是该索引的叶级,并且每个页都有指向前一页的指针和索引顺序中的下一页(它们形成双重链接-list)。

因此,如果您的查询请求与聚簇索引具有相同顺序的行范围...处理器只需要遍历索引一次(或者可能两次),以查找数据的起始页,以及然后按照链接列表指针进入下一页和下一页,直到它读取了所需的所有数据页。

对于非聚集索引,它必须为它检索的每一行遍历索引一次...

注意:编辑
要解决Guid Key列的顺序问题,请注意SQL2k5具有NEWSEQUENTIALID(),它确实以“旧”顺序方式生成Guids。

或者你可以调查在客户端代码中实现的Jimmy Nielsens COMB guid algotithm:

COMB Guids

答案 2 :(得分:5)

GUID字段中聚簇索引的问题是GUID是随机的,因此当插入新记录时,必须移动磁盘上的大部分数据以将记录插入表的中间。

但是,对于基于整数的聚簇索引,整数通常是顺序的(就像IDENTITY规范一样),所以它们只是被添加到最后,没有数据需要移动。

另一方面,聚簇索引在GUID上并不总是坏...这完全取决于应用程序的需求。如果您需要能够快速SELECT条记录,那么请使用聚集索引...... INSERT速度会受到影响,但SELECT速度会有所提升。

答案 3 :(得分:5)

虽然在GUID上进行群集通常是一个坏主意,但请注意GUID在某些情况下可以 cause fragmentation even in non-clustered indexes

请注意,如果您使用的是SQL Server 2005,则newsequentialid()函数会生成顺序 GUID。这有助于防止碎片问题。

我建议在做出任何决定之前使用如下所示的SQL查询来测量碎片(借用非ANSI语法):

SELECT OBJECT_NAME (ips.[object_id]) AS 'Object Name',
       si.name AS 'Index Name',
       ROUND (ips.avg_fragmentation_in_percent, 2) AS 'Fragmentation',
       ips.page_count AS 'Pages',
       ROUND (ips.avg_page_space_used_in_percent, 2) AS 'Page Density'
FROM sys.dm_db_index_physical_stats 
     (DB_ID ('MyDatabase'), NULL, NULL, NULL, 'DETAILED') ips
CROSS APPLY sys.indexes si
WHERE si.object_id = ips.object_id
AND   si.index_id = ips.index_id
AND   ips.index_level = 0;

答案 4 :(得分:4)

如果您使用的是NewId(),则可以切换到NewSequentialId()。这应该有助于插入perf。

答案 5 :(得分:2)

是的,在随机值上有聚集索引是没有意义的。

您可能确实希望数据库中的聚簇索引为SOMEWHERE。例如,如果您有一个“作者”表和一个带有“作者”外键的“书籍”表,并且您的应用程序中有一个查询说“请从书中选择...,其中AuthorId = .. “那么你会读一套书。如果这些书在磁盘上实际上彼此相邻会更快,这样磁盘头就不必在收集该作者所有书籍的部门之间反弹。

因此,您需要考虑您的应用程序,以及查询数据库的方式。

进行更改。

然后测试,因为你永远不知道......

答案 6 :(得分:2)

答案 7 :(得分:0)

是的,您应该删除GUID主键上的聚簇索引,原因是Galwegian在上面说明的原因。我们已经在我们的应用程序上完成了

答案 8 :(得分:0)

这取决于您是否正在进行大量插入,或者您需要通过PK快速查找。

答案 9 :(得分:0)

正如大多数人所提到的,避免在聚集索引中使用随机标识符 - 您将无法获得群集的好处。实际上,您会遇到延迟增加的情况。摆脱所有这些是坚实的建议。还要记住newsequentialid()在多主复制方案中可能非常有问题。如果数据库A和B都在复制之前调用newsequentialid(),则会发生冲突。