关于sql:Clustered vs Non-Clustered

Clustered vs Non-Clustered

我对SQL(Server2008)的较低层次的知识是有限的,现在我们的DBA正在挑战这些知识。我来解释一下(我提到了一些明显的陈述,希望我是对的,但是如果你看到错误,请告诉我)情景:

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

1
2
3
4
5
6
CREATE TABLE dbo.CourtOrder
(
  CourtOrderID INT NOT NULL IDENTITY(1,1), (PRIMARY KEY)
  PersonId INT NOT NULL,
  + around 20 other FIELDS OF different types.
)

然后,我将非聚集索引应用于主键(为了提高效率)。我的理由是,它是一个唯一的字段(主键),应该被索引,主要是为了选择的目的,就像我们经常使用的ecx1〔0〕一样。

然后我对personid应用了一个聚集索引。原因是为了一个特定的人进行身体上的订单分组,因为绝大多数的工作是为一个人获得订单。所以,select from mytable where personId = ...

我现在被拉上来了。我听说我们应该把聚集索引放在主键上,把普通索引放在personid上。我觉得这很奇怪。首先,为什么要在唯一列上放置聚集索引?什么是集群?这肯定是在浪费聚集索引吗?我相信一个普通的索引会被用在一个唯一的列上。此外,聚集索引意味着我们不能聚集不同的列(每个表一个,对吗?).

对于我来说,被告知我犯了一个错误的原因是他们认为在Personid上放置一个聚集索引会使插入变慢。对于一个选择的5%的速度增益,插入和更新的速度将下降95%。正确有效吗?

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

然后我问,如果SQL速度这么慢,它为什么会有聚集索引的概念?它像他们说的那样慢吗?如何设置索引以获得最佳性能?我想选择不仅仅是插入…但他们说我们的插件有锁定问题…

希望有人能帮助我。


聚集索引与非聚集索引的区别在于聚集索引决定了数据库中行的物理顺序。换句话说,将聚集索引应用于PersonId意味着表中的行将由PersonId进行物理排序,从而允许对该行的索引搜索直接进入该行(而不是非聚集索引,它将引导您进入该行的位置,添加额外的步骤)。

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

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


我决不是SQL专家…所以把它作为开发人员的视图,而不是DBA视图。

聚集(物理顺序)索引上的插入不是按顺序排列的,这会导致插入/更新的额外工作。另外,如果有许多插入同时发生,并且它们都发生在同一位置,那么最终会出现争用。您的具体性能因您的数据和访问方式而异。一般的经验法则是在表中最独特的窄值(通常是pk)上构建聚集索引。

我假设你的个人身份不会改变,所以更新不会在这里起作用。但请考虑一下PersonalID为的几行的快照一二三三四五六七八八

现在,为3的personid插入20行新行。首先,由于这不是一个唯一的键,服务器会在您的值(幕后)中添加一些额外的字节,使其具有唯一性(这也会增加额外的空间),然后必须更改这些值所在的位置。将其与插入末尾插入的自动递增pk进行比较。非技术性的解释可能是这样的:如果表格末尾的值自然增加,那么要做的"叶洗刷"工作就更少了,而不是在插入项目的同时在该位置重新加工现有项目的位置。

现在,如果您对插入有问题,那么您可能会同时插入一组相同(或类似)的personid值,这会导致表中不同位置的额外工作,并且碎片会杀死您。在您的例子中,切换到正在集群化的pk的缺点是,如果您今天在整个表中的值分布不同的personid上有插入问题,如果您将集群索引切换到pk,并且所有插入现在都发生在一个位置,那么您的问题实际上可能会因为争用集中度的增加而变得更糟。(另一方面,如果您今天的插入没有分散到所有地方,但通常都集中在类似的区域,那么您的问题很可能会通过将聚集索引从personid切换到pk来缓解,因为您将最小化碎片。)

应根据您的独特情况分析您的绩效问题,并仅将这些类型的答案作为一般指导原则。您最好的选择是依赖DBA,它可以准确地验证您的问题所在。听起来您的资源争用问题可能超出了简单的索引调整。这可能是一个更大问题的症状。(可能的设计问题……否则资源限制。)

无论如何,祝你好运!


一些作者建议,如果存在有利于范围查询的替代方法,不要在identity列上"浪费"CI

根据msdn聚集索引设计指南,应根据以下标准选择密钥

  • 可用于常用查询。
  • 提供高度的独特性。
  • 可用于范围查询。
  • 你的CourtOrderID列与2列相交。你的PersonId13相遇。由于大多数行最后都会添加uniqueifier,所以您也可以声明它是唯一的,并使用PersonId,CourtOrderID,因为它的宽度相同,但更有用的是将聚集索引键作为行定位器添加到所有NCI,这将允许它们覆盖更多的查询。

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


    它在以下链接中进行了解释:https://msdn.microsoft.com/en-us/ms190457.aspx

    群集

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

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

    无聚集

    • 非聚集索引具有与数据行分离的结构。非聚集索引包含非聚集索引键值,每个键值项都有指向包含键值的数据行的指针。

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

    • 可以将非键列添加到非聚集索引的叶级,以旁路现有索引键限制、900字节和16个键列,并执行完全覆盖、索引的查询。


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

    索引-聚集与非聚集

    1
    2
    3
    4
    5
    6
    7
    8
    9
      891 ROWS
      10 sec
      NONCLUSTERED

      OR

      891 ROWS
      14 sec
      CLUSTERED