在SQL Server面向行的存储中,聚集索引和非聚集索引都组织为B树。
(图片来源)
聚集索引和非聚集索引之间的主要区别在于,聚集索引的叶级是表。这有两个含义。
- 聚集索引叶页上的行始终为表中的每个(非稀疏)列包含某些内容(值或指向实际值的指针)。
- 聚集索引是表的主副本。
非聚集索引也可以通过使用INCLUDE
子句(自SQL Server 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 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约定,即将主索引称为聚簇索引。