聚集和非聚集索引实际上是什么意思?

我对数据库的了解有限,只使用过数据库作为应用程序程序员。我想了解ClusteredNon clustered indexes。我用谷歌搜索了一下,发现:

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

我在SO中找到的是聚集索引和非聚集索引之间有什么区别?

有人能用简单的英语解释一下吗?

866635 次浏览

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

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

Customer----------IDNameAddress
Order----------IDCustomerIDPrice

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

附注:CustmerID上的索引显然不是唯一的,因此您需要添加第二个字段来“统一”索引,或者让数据库为您处理,但这是另一回事。

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

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

对于非聚集索引,还有第二个列表具有指向物理行的指针。您可以有许多非聚集索引,尽管每个新索引都会增加写入新记录所需的时间。

如果您想取回所有列,从聚集索引中读取通常会更快。您不必先到索引,然后再到表。

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

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

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

聚集索引

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

非聚集索引

  1. 非聚集索引和简单索引一样,只是用于数据的快速检索,不一定数据唯一

在SQLServer中,面向行的存储聚集索引和非聚集索引都组织为B树。

输入图片描述

图片来源

聚集索引和非聚集索引之间的关键区别在于聚集索引的叶级别表。这有两个含义。

  1. 聚集索引叶页上的行始终为表中的每个(非稀疏)列(值或指向实际值的指针)包含东西
  2. 聚集索引是表的主要副本。

非聚集索引也可以通过使用INCLUDE子句(自SQLServer 2005)显式包含所有非键列来执行第1点,但它们是次要表示,并且始终存在另一个数据副本(表本身)。

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服务器在线书籍引起了很多混乱

在我看来,这将是更好的措辞。

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

这本书的在线引用并不是不正确的,但你应该清楚,非聚集索引和聚集索引的“排序”都是逻辑的,而不是物理的。如果你通过遵循链表在叶级读取页面,并按照槽数组顺序读取页面上的行,那么你将按照排序顺序读取索引行,但物理上的页面可能没有排序。人们普遍认为,对于聚集索引,行总是以与索引关键相同的顺序物理存储在磁盘上,这是错误的。

这将是一个荒谬的实现。例如,如果将一行插入4GB表的中间SQLServer没有必须在文件中复制2GB的数据以为新插入的行腾出空间。

相反,会发生页面拆分。聚集索引和非聚集索引的叶级的每个页面都按逻辑键顺序具有下一页和上一页的地址(File: Page)。这些页面不需要连续或按键顺序排列。

例如,链接的页面链可能是1:2000 <-> 1:157 <-> 1:7053

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

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

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

CREATE TABLE T(X TINYINT NOT NULL,Y CHAR(3000) NULL);
CREATE CLUSTERED INDEX ixON T(X);
GO
--Insert 100 rows with values 1 - 100 in random orderDECLARE @C1 AS CURSOR,@X  AS INT
SET @C1 = CURSOR FAST_FORWARDFOR SELECT numberFROM   master..spt_valuesWHERE  type = 'P'AND number BETWEEN 1 AND 100ORDER  BY CRYPT_GEN_RANDOM(4)
OPEN @C1;
FETCH NEXT FROM @C1 INTO @X;
WHILE @@FETCH_STATUS = 0BEGININSERT 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   TCROSS APPLY sys.fn_PhysLocCracker( %% physloc %% )ORDER  BY page_id

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

输入图片描述

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

跑步后

ALTER INDEX ix ON T REBUILD;

我得到了以下

输入图片描述

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

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

对于后一种情况,如果非聚集索引已经自然地将CI键列作为NCI键列或INCLUDE-d列包含在内,则不添加任何内容。否则,缺失的CI键列会静默添加到NCI中。

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

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

对于未声明为唯一的非聚集索引SQLServer会静默地将行定位器添加到非聚集索引键中。这适用于所有行,而不仅仅是那些实际重复的行。

聚集与非聚集命名法也用于列存储索引

虽然列存储数据并没有真正“聚集”在任何键上,但我们决定保留传统的SQL服务器约定到主索引作为聚集索引。

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

聚类索引

如果你走进一家公共图书馆,你会发现这些书都是按照一个特定的顺序排列的(很可能是杜威十进制系统,或者DDS)。这对应于这些书中的"聚集索引"。如果你想要的书的DDS#是005.7565 F736s,你可以从找到标有001-099或类似标签的那行书架开始。(堆栈末尾的这个结束标志对应于索引中的一个“中间节点”。)最后,你会深入到标有005.7450 - 005.7600的特定书架,然后你会扫描直到找到带有指定DDS#的书,然后你找到了你的书。

非聚类索引

但是,如果你进入图书馆时没有记住你的书的DDS#,那么你就需要另一个索引来帮助你。在过去,你会在图书馆的前面找到一个很棒的抽屉局,被称为“卡片目录”。里面有数千张3x5的卡片——每本书一张,按字母顺序(可能是按标题)排序。这与"非聚集索引"相对应。这些卡片目录以分层结构组织,因此每个抽屉都将标有它包含的卡片范围(例如Ka - Kl;即“中间节点”)。再一次,你会钻取直到你找到你的书,但是在这个的情况下,一旦你找到了它(即“叶节点”),你就没有书本身,只有一张带有索引号(DDS#)的卡片,你可以用它在聚集索引中找到实际的书。

当然,没有什么能阻止图书管理员复印所有卡片,并在一个单独的卡片曲库中以不同的顺序对它们进行排序。(通常至少有两个这样的目录:一个按作者姓名排序,一个按标题排序。)原则上,你可以拥有任意数量的这些“非聚集”索引。

聚集索引

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

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

非集群

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

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

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

聚集索引

聚集索引确定表中数据的物理顺序。因此,表只有一个聚集索引(主键/复合键)。

"字典"不需要任何其他索引,它已经根据单词索引了

非聚集索引

非聚集索引类似于图书中的索引。数据存储在一个地方。索引存储在另一个地方,索引有指向存储位置的指针。这有助于快速搜索数据。因此,一张表有多个非聚集索引。

生物学书”开始时有一个单独的索引指向章节位置,在“结束”处有另一个索引指向公共WORDS位置

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

非聚集索引:非聚集索引的实际数据不能直接在叶节点找到,而是必须采取额外的步骤来查找,因为它只有指向实际数据的行定位器的值。非聚集索引不能作为聚集索引排序。每个表可以有多个非聚集索引,实际上这取决于我们使用的sql服务器版本。基本上Sql server 2005允许249个非聚集索引,对于2008,2016等以上版本,它允许每个表999个非聚集索引。

让我提供一个关于“聚类索引”的教科书定义,它取自数据库系统:全书的15.6.1:

我们也可以说聚类索引,它是一个属性或多个属性的索引,使得该索引的搜索键具有固定值的所有元组出现在大约可以容纳它们的少数块上。

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

在属性a上排序并存储在其中的关系R(a,b)顺序,打包成块,肯定是集群的。a上的索引是聚类索引,因为对于给定的a值a1,所有具有a的值是连续的。因此,它们看起来被打包成块,可能为包含的第一个和最后一个块执行a-值a1,如图15.14所示。然而,b上的索引是不太可能是集群,因为具有固定b值的元组将分布在整个文件中,除非ab的值是#36825;密切相关

Fig 15.14

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

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

为了将两个概念联系在一起,聚类关系可以有聚类索引和非聚类索引。然而,对于非聚类关系,除非索引建立在关系的主键之上,否则聚类索引是不可能的。

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

然而,一些教材喜欢根据聚集文件定义定义聚集索引。这两种类型的定义在聚集关系级别上是相同的,无论它们是根据数据磁盘块还是文件定义聚集关系。从本段的链接来看,

当:属性值A=a的所有元组按顺序(=连续)存储在数据文件中时,文件上属性A的索引是聚类索引

连续存储元组就像说“元组被打包成尽可能少的块来容纳这些元组”一样(一个谈论文件,另一个谈论磁盘)。这是因为连续存储元组是实现“打包成尽可能少的块来容纳这些元组”的方法。

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

非聚集索引-非聚集索引不会对表内的物理数据进行排序。事实上,非聚集索引存储在一个地方,表数据存储在另一个地方。这类似于教科书,书籍内容位于一个地方,索引位于另一个地方。这允许每个表使用多个非聚集索引。索引包含创建索引的列值和该列值所属记录的地址。

聚集索引与非聚集索引的区别

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

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

聚集索引

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

聚集索引

聚簇索引是SQLServer和MySQL中的默认表结构。即使表没有主键,MySQL也会添加隐藏的集群索引,如果表有主键列,SQLServer总是构建聚簇索引。否则,SQL服务器存储为堆表。

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

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

SELECT PostId, TitleFROM PostWHERE PostId = ?

您可以看到执行计划使用聚集索引查找操作来定位包含Post记录的叶节点,并且扫描聚集索引节点只需要两个逻辑读取:

|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

非聚集索引

由于聚簇索引通常是使用主键列值构建的,因此如果要加快使用其他列的查询,则必须添加辅助非聚簇索引。

二级索引将在其叶节点中存储主键值,如下图所示:

不聚集索引

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

CREATE INDEX IDX_Post_Title on Post (Title)

我们执行以下SQL查询:

SELECT PostId, TitleFROM PostWHERE Title = ?

我们可以看到索引查找操作用于在IDX_Post_Title索引中定位叶节点,它可以提供我们感兴趣的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行。