集群与非集群


98

我对SQL(Server 2008)的较低层次的了解是有限的,现在我们的DBA对此提出了挑战。让我解释一下这种情况:(我已经提到一些明显的陈述,希望我是对的,但是如果您发现有问题,请告诉我)。

我们有一张桌子,上面放着人们的“法院命令”。创建表(名称:CourtOrder)时,我的创建方式如下:

CREATE TABLE dbo.CourtOrder
(
  CourtOrderID INT NOT NULL IDENTITY(1,1), (Primary Key)
  PersonId INT NOT NULL,
  + around 20 other fields of different types.
)

然后,我将非聚集索引应用于主键(以提高效率)。我的理由是,它是一个唯一字段(主键),应该像我们经常为主要目的进行索引那样进行索引Select from table where primary key = ...

然后,我在PersonId上应用了CLUSTERED索引。原因是按物理方式将特定人员的订单分组,因为绝大多数工作都在为一个人获取订单。所以,select from mytable where personId = ...

我现在已经对此感到困惑。有人告诉我,应该将聚簇索引放在主键上,而普通索引放在personId上。我觉得这很奇怪。首先,为什么要在唯一列上放置聚集索引?什么是集群?当然这是对聚集索引的浪费吗?我相信普通索引将用于唯一列。同样,对索引进行聚类将意味着我们无法对不同的列进行聚类(每个表一个,对吗?)。

被告知我犯了一个错误的原因是,他们认为在PersonId上放置聚簇索引会使插入速度变慢。对于选择速度提高5%的情况,插入和更新速度将降低95%。那是正确和有效的吗?

他们说,因为我们对personId进行了群集,所以在我们插入或更改PersonId时,SQL Server必须重新排列数据。

所以然后我问,为什么SQL这么慢,为什么会有CLUSTERED INDEX的概念呢?像他们说的那样慢吗?我应该如何设置索引以获得最佳性能?我本以为SELECT的使用比INSERT还要多...但是他们说我们在INSERTS上存在锁定问题...

希望可以有人帮帮我。


Answers:


117

聚集索引与非聚集索引之间的区别在于,聚集索引确定数据库中行的物理顺序。换句话说,将聚簇索引应用于PersonId意味着表中的行将按物理顺序进行排序PersonId,从而允许在该索引上直接搜索到该行(而不是非聚簇索引,后者将您定向到该行的位置,再增加一个步骤)。

也就是说,主键不是聚集索引,但并非闻所未闻是不寻常的。方案的问题实际上与您所假设的相反:您希望聚集索引中的唯一值,而不是重复值。因为聚集索引确定行的物理顺序,所以如果索引位于非唯一列上,则服务器必须向具有重复键值的行添加背景值(在您的情况下,具有相同键值的任何行PersonId),这样组合值(键+背景值)是唯一的。

我唯一建议不要使用代理键(您的CourtOrderId)列作为主键,而应使用PersonId和其他一些唯一标识列或一组列的复合主键。但是,如果这不可能(或不切实际),则将聚簇索引放在上CourtOrderId


谢谢亚当。那么,什么时候聚集索引才有用呢?我以为聚集索引的好处是对数据进行分组,例如在大多数查询都在PersonID上的时候……因此可以对数据进行分组。
Craig

3
不是由物理排序的PersonId。它的逻辑排序方式为PersonId,逻辑顺序与物理顺序之间的任何差异都是逻辑碎片的程度。
马丁·史密斯,

1
@cdotlister索引的好处是数据进行排序,而不是对数据进行分组(这意味着索引中的重复数据)。尽管这种区分似乎是语义上的,但在聚集索引的情况下却不是。如果可能,聚集索引应该位于唯一标识行的事物上,并且(理想情况下)也是最常查询的列或一组列。这就是为什么它通常位于主键上的原因。
亚当·罗宾逊

1
@Cyber​​SluethOmega:我不知道;您的问题所包含的信息不足,无法让我做出决定。我是否要在一组列的聚集索引上,而不是在表的末尾,这些列会经常添加或删除行?不。但是我不太确定您为什么要问这个问题或为什么要投反对票。
亚当·罗宾逊

1
@Cyber​​SluethOmega:互联网可以使注目的目的不是那么冷酷。您声称我说过,在任何情况下我都不知道使聚簇索引成为主键以外的东西,而实际上我没有说过。实际上,我所说的是“这很特殊,但并非闻所未闻”,这意味着我确实知道这样做的情况。
亚当·鲁滨逊

14

我绝不是SQL专家...因此,应将其作为开发人员的观点而不是DBA观点。

不按顺序的聚簇(物理顺序)索引上的插入会导致插入/更新的额外工作。另外,如果您一次执行许多插入操作,并且所有插入操作都发生在同一位置,则最终会导致争用。根据数据和访问方式的不同,您的特定性能也会有所不同。一般的经验法则是在表中最独特的窄值(通常是PK)上建立聚簇索引

我假设您的PersonId不会更改,因此更新在这里不起作用。但是考虑一下PersonId为1 2 3 3 4 5 6 7 8 8的几行快照

现在为3的PersonId插入20个新行。首先,由于这不是唯一键,因此服务器在您的值(位于幕后)中添加了一些额外的字节以使其具有唯一性(这也增加了额外的空间),然后添加了位置这些将保留必须更改。与插入自动增量PK进行比较,在该末尾进行插入。非技术性的解释可能会归结为:如果在表末尾自然地将较高的值进行处理而不是在插入项目时重新加工现有项目在该位置的位置,那么要做的“改组”工作就更少了。

现在,如果您在插入时遇到问题,那么您可能会立即插入一堆相同(或相似)的PersonId值,这会在整个表的各个位置造成额外的工作,而碎片会杀死您。在您的情况下,切换到正在集群的PK的不利之处是,如果您今天在PersonIds上遇到插入问题,该问题在整个表中的价值分布都不同,如果您将聚集的索引切换到PK,并且现在所有插入都一次发生位置,则由于争用集中度的提高,您的问题实际上可能会变得更糟。(另一方面,如果今天您的插入内容并未全部散布,而是通常都聚集在相似的区域,那么您可以通过将聚集索引从PersonId切换到PK来缓解问题,因为这将最大限度地减少碎片。)

应该针对您的独特情况分析您的性能问题,并将这些类型的答案仅作为一般准则。最好的选择是依靠可以准确验证问题所在的DBA。听起来您有资源争用问题,可能超出了简单的索引调整范围。这可能是更大问题的征兆。(可能是设计问题...否则是资源限制。)

无论如何,祝你好运!


5

一些作者确实建议,如果存在可以使范围查询受益的替代方法,则不要“浪费” 列CI上的identity

根据《 MSDN 聚集索引设计指南》,应根据以下条件选择密钥

  1. 可用于经常使用的查询。
  2. 提供高度的独特性。
  3. 可用于范围查询。

您的CourtOrderID专栏碰到了2。你PersonId满足13。由于大多数行最终都会以uniqueifier添加的形式结束,因此您最好将其声明为唯一并使用,PersonId,CourtOrderID因为它的宽度相同,但是由于将簇索引键作为行定位器添加到所有NCI中而更加有用。它们涵盖了更多的查询。

PersonId,CourtOrderID用作CI 的主要问题是可能会发生逻辑碎片(并且这尤其会影响您要帮助的范围查询),因此您需要监视填充因子和碎片级别并更频繁地执行索引维护。


3

在以下链接中对此进行了解释:https : //msdn.microsoft.com/zh-cn/ms190457.aspx

聚类

  • 聚集索引根据它们的键值对数据行进行排序并将其存储在表或视图中。这些是索引定义中包含的列。每个表只能有一个聚集索引,因为数据行本身只能以一种顺序排序。

  • 表中的数据行唯一按排序顺序存储的时间是该表包含聚簇索引时。当表具有聚集索引时,该表称为聚集表。如果表没有聚集索引,则其数据行将存储在称为堆的无序结构中。

非集群

  • 非聚集索引的结构与数据行分开。非聚集索引c 包含非聚集索引键值,并且每个键值条目都有一个指向包含键值的数据行的指针

  • 从非聚集索引中的索引行到数据行的指针称为行定位器。行定位器的结构取决于数据页是存储在堆还是群集表中。对于堆,行定位器是指向该行的指针。对于聚簇表,行定位符是聚簇索引键。

  • 您可以将非关键列添加到非聚集索引的叶级别,以绕过现有索引键限制,900字节和16个关键列,并执行完全覆盖的,建立索引的查询。


-3

一些具有某些讨厌选择的数据库,加入存储过程中-唯一的区别就是索引

索引-集群与非集群

  891 rows
  10 sec
  NONCLUSTERED 

  OR

  891 rows
  14 sec
  CLUSTERED
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.