聚集索引必须是唯一的吗?

Posted

技术标签:

【中文标题】聚集索引必须是唯一的吗?【英文标题】:Do clustered indexes have to be unique? 【发布时间】:2011-05-18 23:50:16 【问题描述】:

如果聚集索引不是唯一的会发生什么?是否会因为插入的行流向某种“溢出”页面而导致性能下降?

它是“制造”独特的吗?如果是,怎么做?让它独一无二的最佳方法是什么?

我问是因为我目前正在使用聚集索引将表划分为逻辑部分,但性能一般,最近我得到了the advice 以使我的聚集索引独一无二。我想对此提出第二意见。

【问题讨论】:

【参考方案1】:

他们没有必须是独一无二的,但肯定是值得鼓励的。 我还没有遇到过要在非唯一列上创建 CI 的场景。

如果你 create a CI on a non-unique column

如果聚集索引不是唯一的 索引,SQL Server 使任何重复 通过在内部添加一个唯一的键 生成的值称为唯一符

这会导致性能不佳吗?

添加一个唯一性肯定会在计算和存储它时增加一些开销。 这种开销是否显着取决于几个因素。

表格包含多少数据。 插入率是多少。 选择中使用 CI 的频率(当不存在覆盖索引时,几乎总是如此)。

编辑 正如 Remus 在 cmets 中指出的那样,确实存在创建非唯一 CI 是合理选择的用例。我没有遇到过这些场景中的任何一种,这只是表明我自己缺乏曝光率或能力(选择你的选择)。

【讨论】:

+1 因为你说的一切都是正确的,但只是想补充一点:当特定(非唯一)列上的范围扫描是流行的访问模式时,非唯一 CI 很常见。 @Remus Rusanu:我当时考虑在我的场景陈述中添加免责声明,例如但这并不意味着什么。感谢您指出一个可能有用的场景。 @Remus:所以你的意思是你有一个像'Departmentid'这样的非唯一列的利基情况,你可以在其中查询'DepartmentId BETWEEN 1 and 100'之类的东西? edit 啊,我明白你的意思了,是的,日志表中的日期列也是一个很好的例子。 嘿,我有一个事件流表,其中多行存在相同的“AggregateId”,这是一个类型 GUID 的列。对表执行的唯一查询是获取给定 AggregateId 的所有事件。我想知道这应该是聚集索引还是非聚集索引? @ShayanC - 如果检索性能是您的主要目标,我会将其设为 CI,以便在检索给定 ID 的所有行时可能会节省 IO。与所有性能场景一样,唯一可靠的方法是测量。【参考方案2】:

聚集索引必须是唯一的吗?

他们不这样做,有时如果他们不这样做会更好。

考虑一个表,其中包含一个半随机的、唯一的 EmployeeId 和每个员工的 DepartmentId:如果您的 select 语句是

SELECT * FROM EmployeeTable WHERE DepartmentId=%DepartmentValue%

那么,如果DepartmentId 是聚集索引,那么即使(或者甚至特别是因为)它不是唯一索引(对性能而言是最佳的,因为它确保给定 DepartmentId 中的所有记录都是聚集的),这对性能来说是最好的。


你有参考吗?

例如Clustered Index Design Guidelines,上面写着,

除了少数例外,每张桌子 应该定义一个聚集索引 在提供的一列或多列上 以下:

可用于常用查询。 提供高度的独特性。 可用于范围查询。

例如,我对“高度唯一性”的理解是,如果您的大多数查询都想选择给定城镇内的记录,那么选择“国家”作为聚集索引是不好的。

【讨论】:

是的,直到现在我都是这么想的,但我也得到了完全相反的建议,所以我想知道哪个是真的。你有参考吗? @littlegreen 我编辑了我的答案,试图回答你的问题。 谢谢。是的,好吧,我明白你的意思了。但是,如果您经常一次插入整个国家/地区,那么(国家,城镇)上的聚集索引对我来说似乎很麻烦,因为它需要对数据进行排序。另一方面,插入之前的排序不会那么麻烦...... 肯定在您的示例中,DepartmentID, EmployeeID 上的唯一聚集索引会更可取吗?当您现有的字段可以以较少的开销提供唯一性(可能是一个四字节的 INT)并且可以让您仅在索引中运行更多查询时,为什么系统要创建一个唯一性?【参考方案3】:

我想看看索引女王金伯利·特里普 (Kimberly Tripp) 对此话题的看法:

我将从我对集群密钥的建议开始 - 有几个原因。首先,这是一个容易做出的决定,其次,尽早做出这个决定有助于主动防止某些类型的碎片化。如果您可以防止某些类型的基表碎片,那么您可以最大限度地减少一些维护活动(其中一些在 SQL Server 2000 中,而在 SQL Server 2005 中则更少)要求您的表处于脱机状态。好的,我稍后再进行重建......

让我们从我在集群键中寻找的关键内容开始:

* Unique
* Narrow
* Static

为什么是独一无二的? 集群键应该是唯一的,因为集群键(如果存在)被用作所有非聚集索引的查找键。以书后的索引为例 - 如果您需要查找索引条目指向的数据 - 该条目(索引条目)必须是唯一的,否则,哪个索引条目将是您要查找的条目?因此,当您创建聚集索引时 - 它必须是唯一的。但是,SQL Server 不需要在唯一列上创建您的集群键。您可以在任何您想要的列上创建它。在内部,如果集群键不是唯一的,那么 SQL Server 将通过向数据添加一个 4 字节整数来“唯一化”它。因此,如果聚集索引是在不唯一的东西上创建的,那么不仅在创建索引时会有额外的开销,还会浪费磁盘空间、INSERT 和 UPDATE 的额外成本,而且在 SQL Server 2000 中,聚集索引会增加成本重建(因为集群键选择不佳,现在更有可能)。

来源: Ever-increasing clustering key debate - again!

【讨论】:

虽然有一个问题,Queen 建议使用 newsequentialid 来统一数据,但如果您不指定,SQL Server 会生成自己的唯一标识符。那么还有什么理由添加你自己的顺序 id 吗? @littlegreen:她说如果你坚持使用 GUID(这对于在集群索引中使用真的很糟糕),那么至少使用 newsequentialid() 来获得几乎顺序化的 GUID。但是是的:如果添加你自己的唯一 ID(我总是更喜欢 INT IDENTITY),那么你手头就有这个值,你可以使用它(例如,建立 FK 关系)。 SQL Server 添加的唯一标识符对您来说是不可见的,因此它们只是您无法使用的开销。 我明白了。好吧,这将是一个支持 (CompanyID, DepartmentID, id INT IDENTITY) 聚集索引的论点,而不仅仅是前两个。谢谢! @littlegreen:更好 - 使您的聚集索引在 (ID INT IDENTITY) 上,并将其他字段 - 如果需要 - 放入单独的非聚集索引中。聚集索引应该尽可能小 - 毕竟,聚集索引列也被添加到该表上每个非聚集索引的每个条目中 - 所以不要浪费你的字节与广泛的聚集索引! 是的,但是我失去了对所有部门数据进行分组的好处,并且我能够一次插入/删除/检索整个部门。我的数据会变得分散,整个部门甚至整个公司的插入/删除都会很慢。我的查询一次只在一家公司上运行,并且经常需要更新整个数据集。

以上是关于聚集索引必须是唯一的吗?的主要内容,如果未能解决你的问题,请参考以下文章

聚集索引和唯一索引的区别是啥?

聚集索引非聚集索引

主键,唯一索引 聚集索引的关系

SQL有三个类型的索引,唯一索引 不能有重复,但聚集索引,非聚集索引可以有重复

数据库怎样创建一个唯一聚集索引

SQL SERVER数据库 唯一索引 非唯一索引 聚集索引 非聚集索引 之间区别