群集和非群集索引实际上意味着什么?

时间:2009-08-09 15:59:42

标签: sql-server performance indexing clustered-index non-clustered-index

我对DB的了解有限,并且只使用DB作为应用程序员。我想了解ClusteredNon clustered indexes。 我用Google搜索,发现的是:

  

聚簇索引是一种特殊类型的索引,可以重新排序   表中的记录是物理的   存储。因此表只能有   一个聚集索引。叶节点   聚集索引包含数据   页面。非聚集索引是   特殊类型的索引   索引的逻辑顺序没有   匹配物理存储的顺序   磁盘上的行。一个叶子节点   非聚集索引不包括   数据页面。相反,叶子   节点包含索引行。

我在SO中发现的是What are the differences between a clustered and a non-clustered index?

有人可以用简单的英语解释这个吗?

12 个答案:

答案 0 :(得分:950)

使用聚簇索引,行以与索引相同的顺序物理存储在磁盘上。因此,只能有一个聚簇索引。

对于非聚集索引,有第二个列表,其中包含指向物理行的指针。虽然每个新索引都会增加写入新记录所需的时间,但您可以拥有许多非聚簇索引。

如果要返回所有列,通常可以更快地从聚簇索引中读取。您不必先进入索引,然后再进入表格。

如果需要重新排列数据,写入具有聚簇索引的表可能会更慢。

答案 1 :(得分:562)

聚簇索引意味着您要告诉数据库在磁盘上存储实际上彼此接近的近似值。这有利于快速扫描/检索落入某些聚集索引值范围的记录。

例如,您有两个表,即客户和订单:

Customer
----------
ID
Name
Address

Order
----------
ID
CustomerID
Price

如果您希望快速检索某个特定客户的所有订单,您可能希望在Order表的“CustomerID”列上创建聚簇索引。这样,具有相同CustomerID的记录将在磁盘(群集)上彼此靠近地物理存储,从而加快了它们的检索速度。

P.S。 CustomerID上的索引显然不是唯一的,因此您需要添加第二个字段来“unquify”索引或让数据库为您处理,但这是另一个故事。

关于多个索引。每个表只能有一个聚簇索引,因为它定义了数据的物理排列方式。如果你想要一个类比,想象一个有很多桌子的大房间。您可以将这些表格放在一起形成多行,也可以将它们全部拉到一起形成一个大型会议桌,但不能同时形成两种方式。一个表可以有其他索引,然后它们将指向聚集索引中的条目,而这些条目最终会说明在哪里可以找到实际数据。

答案 2 :(得分:268)

在SQL Server面向行的存储中,群集和非群集索引都被组织为B树。

enter image description here

Image Source

聚簇索引和非聚簇索引之间的主要区别在于聚簇索引的叶级别表。这有两个含义。

  1. 聚簇索引叶子页面上的行始终包含表中每个(非稀疏)列的某些内容(值或指向实际值的指针)。
  2. 聚集索引是表的主副本。
  3. 非聚簇索引也可以通过使用INCLUDE子句(自SQL Server 2005)来明确包含所有非关键列,但它们是次要表示,并且总是存在另一个数据副本(表本身)。

    CREATE TABLE T
    (
    A INT,
    B INT,
    C INT,
    D INT
    )
    
    CREATE UNIQUE CLUSTERED INDEX ci ON T(A,B)
    CREATE UNIQUE NONCLUSTERED INDEX nci ON T(A,B) INCLUDE (C,D)
    

    上述两个指数几乎相同。上层索引页面包含键列A,B的值和包含A,B,C,D的叶级页面

      

    每个表只能有一个聚簇索引,因为数据行   它们本身只能按一个顺序排序。

    以上来自SQL Server在线书籍引用引起很多混淆

    在我看来,它会更好地表达为。

      

    每个表只能有一个聚簇索引,因为聚簇索引的叶级行是表行。

    图书在线报价并不正确,但您应该清楚"排序"非聚集索引和聚簇索引都是逻辑的而不是物理的。如果按照链接列表读取叶级别的页面并按插槽数组顺序读取页面上的行,那么您将按排序顺序读取索引行,但物理上可能不会对页面进行排序。人们普遍认为,对于聚簇索引,行总是以与索引相同的顺序物理存储在磁盘上。

    这将是一个荒谬的实施。例如,如果在4GB表的中间插入一行,则SQL Server会必须在文件中复制2GB数据,以便为新插入的行腾出空间。

    而是发生页面拆分。聚簇索引和非聚簇索引的叶级别的每个页面都具有逻辑键顺序中下一页和上一页的地址(File:Page)。这些页面不需要是连续的或按键顺序。

    e.g。链接的页面链可能是1:2000 <-> 1:157 <-> 1:7053

    当页面拆分发生时,将从文件组中的任何位置(从混合范围,对于小型表,或属于该对象的非空均匀范围或新分配的统一范围)分配新页面。如果文件组包含多个文件,则这可能不在同一文件中。

    逻辑顺序和连续性与理想化物理版本的不同程度是逻辑碎片的程度。

    在一个新创建的数据库中,我运行了以下文件。

    CREATE TABLE T
      (
         X TINYINT NOT NULL,
         Y CHAR(3000) NULL
      );
    
    CREATE CLUSTERED INDEX ix
      ON T(X);
    
    GO
    
    --Insert 100 rows with values 1 - 100 in random order
    DECLARE @C1 AS CURSOR,
            @X  AS INT
    
    SET @C1 = CURSOR FAST_FORWARD
    FOR SELECT number
        FROM   master..spt_values
        WHERE  type = 'P'
               AND number BETWEEN 1 AND 100
        ORDER  BY CRYPT_GEN_RANDOM(4)
    
    OPEN @C1;
    
    FETCH NEXT FROM @C1 INTO @X;
    
    WHILE @@FETCH_STATUS = 0
      BEGIN
          INSERT INTO T (X)
          VALUES        (@X);
    
          FETCH NEXT FROM @C1 INTO @X;
      END
    

    然后用

    检查页面布局
    SELECT page_id,
           X,
           geometry::Point(page_id, X, 0).STBuffer(1)
    FROM   T
           CROSS APPLY sys.fn_PhysLocCracker( %% physloc %% )
    ORDER  BY page_id
    

    结果到处都是。按键顺序的第一行(值为1 - 用下面的箭头突出显示)几乎在最后一个物理页面上。

    enter image description here

    可以通过重建或重新组织索引来减少或删除碎片,以增加逻辑顺序和物理顺序之间的相关性。

    运行后

    ALTER INDEX ix ON T REBUILD;
    

    我得到以下

    enter image description here

    如果表没有聚集索引,则称为堆。

    可以在堆或聚簇索引上构建非聚簇索引。它们总是包含一个返回基表的行定位器。在堆的情况下,这是一个物理行标识符(rid),由三个组件组成(File:Page:Slot)。对于聚簇索引,行定位器是逻辑的(聚簇索引键)。

    对于后一种情况,如果非聚集索引已经自然地将CI密钥列包括为NCI密钥列或INCLUDE - d列,则不添加任何内容。否则,缺少的CI密钥列将无声地添加到NCI中。

    SQL Server始终确保键列对于两种类型的索引都是唯一的。然而,对于未声明为唯一的索引强制执行此操作的机制在两种索引类型之间有所不同。

    对于具有复制现有行的键值的任何行,聚簇索引都会添加uniquifier。这只是一个递增的整数。

    对于未声明为唯一的非聚簇索引,SQL Server以静默方式将行定位符添加到非聚簇索引键中。这适用于所有行,而不仅仅是那些实际重复的行。

    群集与非群集命名法也用于列存储索引。论文Enhancements to SQL Server Column Stores陈述

      

    虽然列存储数据并非真正&#34;集群&#34;在任何关键,我们   决定保留传统的SQL Server约定   作为聚集索引的主索引。

答案 3 :(得分:118)

我意识到这是一个非常古老的问题,但我想我会提供一个类比来帮助说明上面的好答案。

CLUSTERED INDEX

如果你走进一个公共图书馆,你会发现这些书都按照特定的顺序排列(很可能是杜威十进制系统或DDS)。这对应于书籍的&#34;聚集索引&#34; 。如果您想要的图书的DDS#是005.7565 F736s,那么您首先要找到标有001-099的书架或类似的书架。 (堆栈末尾的此endcap符号对应于索引中的&#34;中间节点&#34;。)最后,您将深入到标记为005.7450 - 005.7600的特定架子,然后您将扫描直到找到这本书带有指定的DDS#,并且在那时你找到了你的书。

非集群指数

但是如果你没有记录你书中的DDS#进入图书馆,那么你需要第二个索引来帮助你。在过去的日子里,你会发现在图书馆前面有一个很棒的抽屉柜,被称为&#34;卡片目录&#34;。其中有数千张3x5卡片 - 每本书一张,按字母顺序排列(可能是标题)。这对应于&#34;非聚集索引&#34; 。这些卡片目录以分层结构组织,因此每个抽屉将标有其包含的卡片范围(例如Ka - Kl;即&#34;中间节点&#34;)。再一次,你会钻进去,直到你找到你的书,但在这个的情况下,一旦找到它(即&#34;叶节点&#34;),你就不会这样做。这本书本身,但只是一张带有索引号码的卡片(DDS#),你可以用它找到聚集索引中的实际书籍。

当然,没有什么可以阻止图书管理员复印所有卡片并在单独的卡片目录中以不同的顺序对它们进行分类。 (通常至少有两个这样的目录:一个按作者名称排序,一个按标题排序。)原则上,你可以拥有尽可能多的这些&#34;非群集&#34;索引你想要的。

答案 4 :(得分:66)

在下面找到群集和非群集索引的一些特征:

聚集索引

  1. 聚簇索引是唯一标识SQL表中行的索引。
  2. 每个表只能有一个聚簇索引。
  3. 您可以创建一个涵盖多个列的聚簇索引。例如:create Index index_name(col1, col2, col.....)
  4. 默认情况下,具有主键的列已具有聚簇索引。
  5. 非聚集索引

    1. 非聚集索引就像简单索引。它们仅用于快速检索数据。不确定是否有独特的数据。

答案 5 :(得分:45)

一个非常简单的非技术性经验法则是聚簇索引通常用于您的主键(或者至少是一个唯一的列),非聚集索引用于其他情况(可能是外键)。实际上,SQL Server默认会在主键列上创建聚簇索引。正如您将了解到的,聚集索引与数据在磁盘上的物理排序方式有关,这意味着它在大多数情况下都是一个很好的全面选择。

答案 6 :(得分:23)

聚集索引

聚簇索引确定表中DATA的物理顺序。因此,表只有1个聚簇索引。

喜欢&#34;字典&#34;不需要任何其他索引,根据单词已经索引

非聚集索引

非聚集索引类似于Book中的索引。数据存储在一个位置。该 index存储在另一个地方,索引具有指向数据存储位置的指针。因此,一个表有超过1个Nonclustered索引。

喜欢&#34;化学书&#34;在凝视时,有一个单独的索引指向章节位置和在&#34;结束&#34;还有另一个索引指向常见的WORDS位置

答案 7 :(得分:6)

聚集索引

聚集索引基本上是一个树状组织的表。聚簇索引实际上不是将记录存储在未排序的堆表空间中,而是具有叶节点的 B+树索引,叶节点按簇键列值排序,存储实际表记录,如下图所示。< /p>

Clustered Index

聚集索引是 SQL Server 和 MySQL 中的默认表结构。尽管即使表没有主键,MySQL 也会添加隐藏的簇索引,但如果表具有主键列,SQL Server 始终会构建一个簇索引。否则,SQL Server 将存储为堆表。

聚簇索引可以加速通过聚簇索引键过滤记录的查询,就像通常的 CRUD 语句一样。由于记录位于叶节点中,因此在按主键值定位记录时无需额外查找额外的列值。

例如,在 SQL Server 上执行以下 SQL 查询时:

SELECT PostId, Title
FROM Post
WHERE PostId = ? 

可以看到执行计划使用了Clustered Index Seek操作来定位包含Post记录的Leaf Node,扫描Clustered Index节点只需要两次逻辑读取:

|StmtText                                                                             |
|-------------------------------------------------------------------------------------|
|SELECT PostId, Title FROM Post WHERE PostId = @P0                                    |
|  |--Clustered Index Seek(OBJECT:([high_performance_sql].[dbo].[Post].[PK_Post_Id]), |
|     SEEK:([high_performance_sql].[dbo].[Post].[PostID]=[@P0]) ORDERED FORWARD)      | 

Table 'Post'. Scan count 0, logical reads 2, physical reads 0

非聚集索引

由于聚集索引通常是使用主键列值构建的,如果您想加快使用其他列的查询速度,则必须添加辅助非聚集索引。

Secondary Index 将把 Primary Key 值存储在它的 Leaf Nodes 中,如下图所示:

Non-Clustered Index

因此,如果我们在 Title 表的 Post 列上创建二级索引:

CREATE INDEX IDX_Post_Title on Post (Title)

然后我们执行以下 SQL 查询:

SELECT PostId, Title
FROM Post
WHERE Title = ? 

我们可以看到一个Index Seek操作用于定位IDX_Post_Title索引中的Leaf Node,可以提供我们感兴趣的SQL查询投影:

|StmtText                                                                      |
|------------------------------------------------------------------------------|
|SELECT PostId, Title FROM Post WHERE Title = @P0                              |
|  |--Index Seek(OBJECT:([high_performance_sql].[dbo].[Post].[IDX_Post_Title]),|
|     SEEK:([high_performance_sql].[dbo].[Post].[Title]=[@P0]) ORDERED FORWARD)|

Table 'Post'. Scan count 1, logical reads 2, physical reads 0

由于关联的 PostId 主键列值存储在 IDX_Post_Title 叶节点中,因此该查询不需要额外查找来定位聚集索引中的 Post 行。

答案 8 :(得分:3)

聚集索引

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

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

<强>非聚集

非聚簇索引具有与数据行分开的结构。非聚簇索引包含非聚簇索引键值,每个键值条目都有一个指向包含键值的数据行的指针。 从非聚簇索引中的索引行到数据行的指针称为行定位器。行定位器的结构取决于数据页是存储在堆还是聚簇表中。对于堆,行定位器是指向该行的指针。对于集群表,行定位器是聚簇索引键。

您可以将非键列添加到非聚簇索引的叶级别,以绕过现有索引键限制,并执行完全覆盖的索引查询。有关更多信息,请参阅使用包含的列创建索引。有关索引键限制的详细信息,请参阅SQL Server的最大容量规范。

参考:https://docs.microsoft.com/en-us/sql/relational-databases/indexes/clustered-and-nonclustered-indexes-described

答案 9 :(得分:2)

聚集索引-聚集索引定义了数据在表中的物理存储顺序。表数据只能以某种方式排序,因此,每个表只能有一个聚集索引。在SQL Server中,主键约束会在该特定列上自动创建聚簇索引。

非聚集索引-非聚集索引不会对表中的物理数据进行排序。实际上,非聚集索引存储在一个位置,表数据存储在另一位置。这类似于教科书,其中书的内容位于一个位置,索引位于另一个位置。这样一来,每个表可以有一个以上的非聚集索引。重要的是要在表中提到,数据在表内将按聚集索引进行排序。但是,内部非聚集索引数据按指定顺序存储。索引包含创建索引的列值和该列值所属的记录的地址。对创建索引的列进行查询时,数据库将首先转到索引并查找表中相应行的地址。然后它将转到该行地址并获取其他列值。正是由于这一额外的步骤,非聚簇索引比聚簇索引慢

聚集索引和非聚集索引之间的差异

  1. 每个表只能有一个聚集索引。但是,您可以 在单个表上创建多个非聚集索引。
  2. 聚集索引仅对表进行排序。因此,它们不消耗 额外的存储空间。非聚集索引存储在单独的位置 从实际表中声称拥有更多存储空间。
  3. 聚集索引比非聚集索引更快,因为它们 不需要任何额外的查找步骤。

有关更多信息,请参阅this文章。

答案 10 :(得分:1)

聚集索引: 如果表上不存在聚簇索引,则主键约束会自动创建聚簇索引。聚簇索引的实际数据可以存储在索引的叶级。

非聚集索引: 非聚簇索引的实际数据不能在叶节点上直接找到,而是必须采取额外的步骤才能找到,因为它仅具有指向实际数据的行定位符的值。 非聚集索引不能作为聚集索引排序。每个表可以有多个非聚集索引,实际上这取决于我们使用的sql server版本。基本上,SQL Server 2005允许249个非聚簇索引,而对于像2008、2016这样的更高版本,每个表允许999个非聚簇索引。

答案 11 :(得分:0)

让我提供一个有关“聚簇索引”的教科书定义,该定义取自Database Systems: The Complete Book的15.6.1:

  

我们也可以说聚簇索引,它们是一个或多个属性上的索引,因此对于该索引的搜索关键字而言,具有固定值的所有元组都尽可能少地出现在抱住他们。

要理解该定义,让我们看一下教科书提供的示例15.10:

  

根据属性R(a,b)排序并存储在其中的关系a   打包成块的订单肯定是集群的。 a上的索引是   聚簇索引,因为对于给定的a值a1,所有元组都具有   a的值是连续的。因此,它们似乎包装成   块,除了可能包含的第一个和最后一个块   如图15.14所示,a值a1。但是,b的索引是   不太可能聚集,因为具有固定b值的元组   除非ab的值是   紧密相关。

Fig 15.14

请注意,该定义并不强制数据块必须在磁盘上是连续的;它只说带有搜索键的元组被打包到尽可能少的数据块中。

一个相关的概念是集群关系。如果一个关系的元组打包成尽可能少的块,则该关系将被“聚集”。换句话说,从磁盘块的角度来看,如果它包含来自不同关系的元组,则这些关系不能被聚类(即,存在一种更打包的方式来存储这种关系,方法是将该关系的元组与其他磁盘块中的元组交换为元组(不属于当前磁盘块中的关系)。显然,以上示例中的R(a,b)是集群的。

要将两个概念连接在一起,一个聚簇关系可以具有一个聚簇索引和一个非聚簇索引。但是,对于非聚集关系,除非索引建立在该关系的主键之上,否则不可能建立聚簇索引。

“集群”一词在数据库存储端的所有抽象级别(三个抽象级别:元组,块,文件)中都是垃圾邮件。称为“ clustered file”的概念,它描述文件(一组块(一个或多个磁盘块)的抽象)是否包含来自一个关系或不同关系的元组。它与文件级别上的聚簇索引概念无关。

但是,有些teaching material喜欢基于聚类文件定义来定义聚簇索引。这两种类型的定义在集群关系级别上都是相同的,无论它们是根据数据磁盘块还是文件来定义集群关系。在本段的链接中,

  

在以下情况下,文件上属性A的索引是聚簇索引:具有属性值A = a的所有元组顺序(=连续)存储在数据文件中

连续存储元组与说“元组被打包到尽可能少的块中,可以容纳这些元组”相同(一个在谈论文件,另一个在谈论磁盘,差别不大)。这是因为连续存储元组是实现“打包到可能容纳这些元组的尽可能少的块中”的方法。