协慌网

登录 贡献 社区

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

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

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

我在 SO 中发现的是聚簇索引和非聚簇索引之间有什么区别?

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

答案

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

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

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

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

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

例如,您有两个表,Customer 和 Order:

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

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

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

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

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

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

在此输入图像描述

图片来源

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

  1. 聚集索引的叶页上的行总是包含在桌上的东西对每个(非稀疏)的列(或价值,或指向的实际值)。
  2. 聚簇索引是表的主副本。

非聚簇索引也可以通过使用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 )。这些页面不必是连续的或按键顺序。

例如,链接的页链可能是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 - 用下面的箭头突出显示)几乎在最后一个物理页面上。

在此输入图像描述

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

跑完之后

ALTER INDEX ix ON T REBUILD;

我得到了以下内容

在此输入图像描述

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

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

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

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

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

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

聚簇与非聚集命名法也用于列存储索引。本文增强了 SQL Server 列存储的状态

虽然列存储数据并未真正 “聚集” 在任何键上,但我们决定保留传统的 SQL Server 约定,将主索引称为聚簇索引。