如何在SQL Server中选择聚集索引?

11

通常在SQL Server Management Studio中,通过设置主键来创建聚集索引,但是我的最近一个关于PK <-> 聚集索引 (Meaning of Primary Key to Microsoft SQL Server 2008) 的问题表明,将PK和聚集索引设置为相等并非必要。

那么我们应该如何选择聚集索引呢?让我们看以下示例:

create table Customers (ID int, ...)
create table Orders (ID int, CustomerID int)

通常我们会在两个ID列上创建主键/唯一约束,但我考虑在CustomerID上为Orders创建它。这是最好的选择吗?


可能是SQL Server - 何时使用聚集索引和非聚集索引?的重复问题。 - Michael Freidgeim
3个回答

13

根据索引女王Kimberly Tripp所说,她在聚集索引中主要关注以下几点:

  • 唯一性
  • 窄度
  • 静态性

如果你还能保证:

  • 递增模式

那么你就非常接近理想的聚集键了!

请查看她的整篇博客文章,以及另一篇有关聚集键对表操作影响的非常有趣的文章:聚集索引辩论继续

像INT(特别是自动增量的INT)或可能是INT和DATETIME这样的字段是理想的候选项。出于其他原因,GUID不是好的选择 - 因此您可以将GUID作为PK,但不要在其上进行聚集 - 它将被碎片化并导致性能下降。


这些博客文章对于更新版本的SQL Server仍然同样相关吗?或者说,SQL Server 2008及以后的性能调整是否在某种程度上改变了最佳实践? - Adrian Grigore
@AdrianGrigore:只要使用“普通”表(例如,不是数据仓库/列存储等),一切仍然有效。 - marc_s
@marc_s 感谢您的指南,我也读了Kimberly的文章,但我感到困惑,希望您能帮助我。她说使用默认的sequentialId() GUID是可以的,而您则表示它们根本不是好的选择。我在考虑是否在我的表上没有聚集索引或者在顺序GUID上有聚集索引。 - Mostafa
1
@Mostafa:Guids总是不好的,因为它们太大了(比bigint大两倍,比int大四倍)。使用newsequentialid()至少它们不是完全随机的——这比使用newid(),但仍然比intbigint差。如果您必须出于某种原因使用guid,请确保将newsequentialid()用作该列的默认子句。但是,如果您可以的话,最好完全避免使用guid作为聚集键 - marc_s
我认为最大的问题是碎片化,甚至比大小更重要。 - Ernesto

6

最适合用作CLUSTERED索引的候选键是您最常用于引用记录的键。

通常,这是一个PRIMARY KEY,因为它用于搜索和/或FOREIGN KEY关系。

在您的情况下,Orders.ID很可能参与搜索和引用,因此它是最适合成为聚集表达式的候选者。

如果您在Orders.CustomerID上创建了CLUSTERED索引,则会发生以下事情:

  1. CustomerID is not unique. To ensure uniqueness, a special hidden 32-bit column known as uniquifier will be added to each record.

  2. Records in the table will be stored according to this pair of columns (CustomerID, uniquifier).

  3. A secondary index on Order.ID will be created, with (CustomerID, uniquifier) as the record pointers.

  4. Queries like this:

    SELECT  *
    FROM    Orders
    WHERE   ID = 1234567
    

    will have to do an external operation, a Clustered Seek, since not all columns are stored in the index on ID. To retrieve all columns, the record should first be located in the clustered table.

这个额外的操作需要与简单的Clustered Seek相同的IndexDepth数量的页面读取,其中IndexDepth是您的表中记录总数的O(log(n)).


1

如果您关心聚类,通常是为了帮助改善数据检索。在您的例子中,您可能想要一次获取给定客户的所有记录。按照客户ID进行聚类将使这些行保持在同一页上,而不是分散在文件的多个页面中。

ROT: 按照您想要显示的集合进行聚类。采购订单中的行项目是经典示例。


采购订单上的行项目可能对于一个集群来说是个好主意,但如果典型订单中只有2或3个(或十几个)行项目,则不是。除非您要将要聚类在一起的行数增加到几十个或几百个,否则最好让SQL Server执行书签查找。我曾经有一个系统,业务需求是找到特定收银员班次期间发生的所有“行项目”(以查看他们是否平衡)。通过将“行项目”与Shiftid去规范化,然后在Shift上进行聚类,可以大大提高速度。 - Ian Boyd

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