我应该删除Guid列上的聚集索引吗?

39

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

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

你认为,我应该摆脱所有聚集索引,并用非聚集索引取代它们吗?

为什么SQL的性能调整程序不会将此作为建议呈现?


1
请看以下Paul Randal的帖子:在随机GUID上创建聚集索引或非聚集索引? - LCJ
9个回答

38

对于聚集索引的重要原因之一是,当您经常需要检索给定列的一系列值的行时。由于数据以该顺序物理排列,因此可以非常有效地提取行。

像 GUID 这样的内容,虽然在主键方面表现出色,但可能会对性能产生显著的负面影响,因为插入操作将具有额外成本,而选择操作则没有明显的好处。

因此,不要在 GUID 上聚集索引。

至于为什么它不作为推荐选项提供,我建议调整器应该意识到这一点。


4
使用SQL 2005和newsequentialid(),可以在很大程度上解决碎片问题。最好通过查看sys.dm_db_index_physical_stats和sys_indexes进行衡量。 - HTTP 410
尽管如此,您的查询仍然没有任何好处。如果需要,您应该只在“UNIQUEIDENTIFIER”上进行聚类,例如用于复制。 - Jonathan Gilbert

27
你几乎肯定希望在数据库中的每个表上建立聚集索引。如果一个表没有聚集索引,它被称为“堆”,大多数常见查询的性能比聚集索引表。应该在哪些字段上建立聚集索引取决于表本身和针对表的查询的预期使用模式。几乎在每种情况下,您可能希望将聚集索引放在一个列或一组列上,这是唯一的,即(替代键),因为如果不是这样,SQL将在所选字段的结尾添加一个唯一值。如果您的表中有一个或多个列经常被查询用于选择或过滤多条记录(例如,如果您的表包含销售事务,并且您的应用程序会频繁请求按产品ID的销售事务,或者更好的是,发票明细表,在几乎每种情况下,您都将检索特定发票的所有详细记录,或者发票表,其中您经常检索某个客户的所有发票...这是真实的,无论您是通过单个值还是通过一系列值选择大量记录),则这些列是聚集索引的候选列。聚集索引中列的顺序非常重要。在索引中定义的第一列应该是预期查询中首先选择或过滤的列。
所有这一切的原因都基于对数据库索引内部结构的理解。这些索引被称为平衡树(B-Tree)索引。它们有点像二叉树,但是树中的每个节点可以有任意数量的条目(和子节点),而不仅仅是两个。聚集索引的不同之处在于,聚集索引中的叶节点是表本身的实际物理磁盘数据页。而非聚集索引的叶节点只是“指向”表的数据页。
因此,当一个表具有聚集索引时,该索引的叶级别就是表的数据页,每个数据页都有指向索引顺序中前一页和后一页的指针(它们形成一个双向链表)。
因此,如果您的查询请求的行范围与聚集索引的顺序相同...处理器只需要遍历一次索引(或可能两次),找到数据的起始页,然后按顺序跟随链接列表指针获取下一页和下一页,直到读取所需的所有数据页。
对于非聚集索引,它必须为检索的每一行遍历一次索引...
注意:编辑
为了解决Guid Key列的顺序问题,请注意SQL2k5有NEWSEQUENTIALID(),它确实以“旧”的顺序生成Guids。
或者您可以调查Jimmy Nielsens在客户端代码中实现的COMB guid算法: COMB Guids

3
GUID是怎样的呢?除非它们是连续的GUID,否则您将永远无法按与聚集索引相同的顺序检索一系列行。因此我的问题是什么。 - cbp
你说得对,一般情况下,当需要获取非索引列时,非聚集索引比聚集索引在单行访问时略快。然而,对于“覆盖”索引来说,这并不重要。(续) - Charles Bretana
2
但是,即使您使用非连续的 GUID,聚集索引也可以帮助查询“数据组”。例如,如果 GUID 是父表中的 PK,并且是子表中复合聚集索引 PK 的第一个(FK)列,则所有聚集索引的优点都适用。 - Charles Bretana
此外,您可以创建连续的 GUID。请参阅 http://www.yafla.com/dennisforbes/Sequential-GUIDs-in-SQL-Server/Sequential-GUIDs-in-SQL-Server.html。 - Charles Bretana

10
在GUID字段上使用聚集索引的问题在于GUID是随机的,所以当插入新记录时,需要将磁盘上的大部分数据移动到表的中间位置来插入记录。
然而,对于基于整数的聚集索引,整数通常是顺序的(比如使用IDENTITY规范),因此它们只会被添加到末尾,不需要移动任何数据。
另一方面,聚集索引并不总是对GUID不利...这完全取决于您的应用程序的需求。如果您需要快速选择记录,则使用聚集索引...插入速度可能会受到影响,但选择速度将得到改善。

5
尽管在GUID上进行聚集通常是一个不好的主意,但要注意,在某些情况下,GUIDs甚至可以导致非聚集索引的碎片化问题。
请注意,如果您使用的是SQL Server 2005,newsequentialid()函数会生成顺序的GUIDs。这有助于解决碎片化问题。
我建议在做出任何决策之前,使用以下类似的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
如果您正在使用NewId(),可以切换到NewSequentialId()。这应该有助于插入性能。

3

有一个随机值的聚集索引是没有意义的。

你可能确实需要在数据库中的某些地方使用聚集索引。例如,如果你有一个“作者”表和一个“图书”表,其中“图书”表有一个外键指向“作者”,并且如果你的应用程序中有一个查询语句:“select ... from Book where AuthorId = ..”,那么你将读取一组书籍。如果这些书籍在物理上相邻,磁盘头就不必在扇区之间反复跳动,收集该作者的所有书籍,这样速度会更快。

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

进行更改。

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


1
大多数人都提到了,在聚集索引中避免使用随机标识符-您将无法获得聚集的好处。实际上,您会遇到更长的延迟。摆脱所有这些是明智的建议。还要记住,在多主复制场景中,newsequentialid()可能会带来极大的问题。如果数据库A和B在复制之前都调用newsequentialid(),就会发生冲突。

0

是的,你应该根据Galwegian上述的原因删除GUID主键上的聚集索引。我们在我们的应用程序中已经这样做了。


0

这取决于您是否需要进行大量插入,或者是否需要通过主键进行非常快速的查找。


聚类不会影响查找速度 - 一个唯一的非聚集索引就可以胜任。 - Mike Woodhouse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接