我对DB的接触有限,只是作为应用程序程序员使用过DB。我想知道关于聚集和非聚集索引。 我在谷歌上搜索了一下,我发现:

A clustered index is a special type of index that reorders the way records in the table are physically stored. Therefore table can have only one clustered index. The leaf nodes of a clustered index contain the data pages. A nonclustered index is a special type of index in which the logical order of the index does not match the physical stored order of the rows on disk. The leaf node of a nonclustered index does not consist of the data pages. Instead, the leaf nodes contain index rows.

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

有人能用通俗易懂的语言解释一下吗?


当前回答

聚集索引

聚类索引决定了表中DATA的物理顺序。因此,一个表只有一个聚集索引(主键/组合键)。

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

非聚集索引

非聚集索引类似于Book中的索引。数据存储在一个地方。索引存储在另一个位置,并且索引具有指向存储位置的指针。这有助于快速搜索数据。因此,一个表有超过1个非聚集索引。

“生物书”在开头有一个单独的索引指向章节的位置,在“结尾”有另一个索引指向常用单词的位置

其他回答

聚集索引

聚集索引基本上是一个树状组织的表。与将记录存储在未排序的Heap表空间中不同,聚集索引实际上是具有叶子节点(按群集键列值排序)的B+树索引,存储实际的表记录,如下图所示。

在SQL Server和MySQL中,Clustered Index是默认的表结构。即使表没有主键,MySQL也会添加一个隐藏的集群索引,而SQL Server总是在表有主键列时构建一个集群索引。否则,SQL Server将存储为堆表。

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

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

SELECT PostId, Title
FROM Post
WHERE 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列上创建一个Secondary Index:

CREATE INDEX IDX_Post_Title on Post (Title)

然后执行下面的SQL查询:

SELECT PostId, Title
FROM Post
WHERE 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行。

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

(图片来源)

聚类索引与非聚类索引之间的关键区别在于,聚类索引的叶级是表。这有两个含义。

聚集索引叶页上的行总是包含表中每个(非稀疏)列的内容(值或指向实际值的指针)。 聚集索引是表的主副本。

非聚集索引也可以通过使用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在线书籍引起了很多混乱

在我看来,这样说更好。

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

The book's online quote is not incorrect but you should be clear that the "sorting" of both non clustered and clustered indices is logical, not physical. If you read the pages at leaf level by following the linked list and read the rows on the page in slot array order then you will read the index rows in sorted order but physically the pages may not be sorted. The commonly held belief that with a clustered index the rows are always stored physically on the disk in the same order as the index key is false.

这将是一个荒谬的实现。例如,如果一行被插入到一个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总是确保这两种类型的索引的键列是唯一的。但是,对于没有声明为唯一的索引,两种索引类型的强制机制是不同的。

聚集索引将为键值与现有行的重复的任何行添加唯一符。这只是一个升序整数。

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

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

尽管列存储数据并没有真正地“聚集”在任何键上,但是我们 决定保留传统的SQL Server引用约定 添加到主索引作为聚集索引。

聚集索引意味着您告诉数据库在磁盘上存储实际上彼此接近的接近值。这样做的好处是可以快速扫描/检索某些聚集索引值范围内的记录。

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

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

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

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

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

Regarding multiple indexes. You can have only one clustered index per table because this defines how the data is physically arranged. If you wish an analogy, imagine a big room with many tables in it. You can either put these tables to form several rows or pull them all together to form a big conference table, but not both ways at the same time. A table can have other indexes, they will then point to the entries in the clustered index which in its turn will finally say where to find the actual data.

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

Non Clustered Index: Actual data of non clustered index is not directly found at leaf node, instead it has to take an additional step to find because it has only values of row locators pointing towards actual data. Non clustered Index can't be sorted as clustered index. There can be multiple non clustered indexes per table, actually it depends on the sql server version we are using. Basically Sql server 2005 allows 249 Non Clustered Indexes and for above versions like 2008, 2016 it allows 999 Non Clustered Indexes per table.

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

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

如果想要返回所有列,从聚集索引中读取通常更快。您不必先访问索引,再访问表。

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