唯一聚集索引上的唯一和非唯一非聚集索引

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介:

在上篇文章里,我讨论了唯一和非唯一聚集索引的区别。我们已经知道,SQL Server内部使用4 bytes的uniquifier来保证非唯一聚集索引行唯一。今天我们来看下唯一聚集索引上,唯一和非唯一非聚集索引的区别。当我们在表上定义PRIMARY KEY约束时,SQL Server会为我们创建唯一聚集索引;另外我们可以通过CREATE UNIQUE CLUSTERED INDEX语句在表上创建唯一聚集索引。下面的代码会创建customers表,然后在它上面创建唯一聚集索引,最后在表上创建唯一和非唯一非聚集索引。

复制代码
 1 -- Create a table with 393 length + 7 bytes overhead = 400 bytes 
 2 -- Therefore 20 records can be stored on one page (8.096 / 400) = 20,24 
 3 CREATE TABLE Customers 
 4 ( 
 5     CustomerID INT NOT NULL, 
 6     CustomerName CHAR(100) NOT NULL, 
 7     CustomerAddress CHAR(100) NOT NULL, 
 8     Comments CHAR(189) NOT NULL 
 9 ) 
10 GO
11   
12 -- Create a unique clustered index on the previous created table 
13 CREATE UNIQUE CLUSTERED INDEX idx_Customers ON Customers(CustomerID) 
14 GO  
15 -- Insert 80.000 records 
16 DECLARE @i INT = 1 
17 WHILE (@i <= 80000) 
18 BEGIN 
19     INSERT INTO Customers VALUES 
20     ( 
21         @i, 
22         'CustomerName' + CAST(@i AS CHAR), 
23         'CustomerAddress' + CAST(@i AS CHAR), 
24         'Comments' + CAST(@i AS CHAR) 
25     )     
26     SET @i += 1 
27 END 
28 GO
29 
30 -- Create a unique non clustered index on the clustered table 
31 CREATE UNIQUE NONCLUSTERED INDEX idx_UniqueNCI_CustomerID 
32 ON Customers(CustomerName) 
33 GO  
34 
35 -- Create a non-unique non clustered index on the clustered table 
36 CREATE NONCLUSTERED INDEX idx_NonUniqueNCI_CustomerID 
37 ON Customers(CustomerName) 
38 GO
复制代码

在2个非聚集索引创建后,我们可以使用DMV sys.dm_db_index_physical_stats来查看索引的相关信息。

复制代码
 1 -- Retrieve physical information about the unique non-clustered index 
 2 SELECT * FROM sys.dm_db_index_physical_stats 
 3 ( 
 4     DB_ID('ALLOCATIONDB'), 
 5     OBJECT_ID('Customers'), 
 6     2, 
 7     NULL, 
 8     'DETAILED'
 9 ) 
10 GO
11 
12 -- Retrieve physical information about the non-unique non-clustered index 
13 SELECT * FROM sys.dm_db_index_physical_stats 
14 ( 
15     DB_ID('ALLOCATIONDB'), 
16     OBJECT_ID('Customers'), 
17     3, 
18     NULL, 
19     'DETAILED'
20 ) 
21 GO
复制代码

我们可以看到,唯一非聚集索引的记录长度是107 bytes,非唯一非聚集索引的记录长度是111 bytes。因此这2个索引的内部存储格式肯定不同。我们从唯一非聚集索引开始分析。

我们可以通过DBCC IND命令找出索引根页,聚集索引的INDEX ID为1,非聚集索引的INDEX ID从2开始,依次递增,这里应该是2和3。

复制代码
1 TRUNCATE TABLE dbo.sp_table_pages
2 INSERT INTO dbo.sp_table_pages
3 EXEC('DBCC IND(ALLOCATIONDB, Customers, -1)') 
4 
5 SELECT * FROM dbo.sp_table_pages ORDER BY IndexLevel DESC
复制代码

从这里我们可以看出,唯一非聚集索引的根页是20834,非唯一非聚集索引的根页是21890。

我们看下唯一非聚集索引的根页内容:

1 DBCC PAGE(ALLOCATIONDB, 1, 20834, 3)
2 GO

从图中我们可以看到,每条索引记录包含非聚集键(这里是唯一的)——即CustomerName列。

我们换参数1再来看看根页信息:

1 DBCC TRACEON(3604)
2 DBCC PAGE(ALLOCATIONDB, 1, 20834, 1)
3 GO

这里的107 bytes包含下列信息:

  • 1 byte: 状态位
  • n bytes:非唯一聚集索引键——这里是CustomerName列,100 bytes
  • 4 bytes:页ID(PageID)
  • 2 bytes:文件ID(FileID)

在唯一非聚集索引里,所有非叶子层的每条索引记录都包含这107 bytes信息。因此,你的非聚集索引键大小会影响到每个索引页可以存储多少条索引记录。这样的话,这个例子的CHAR(100),并不是一个很好的索引键。

我们继续往下看,索引叶子层的存储情况:

复制代码
1 DBCC PAGE(ALLOCATIONDB, 1, 20834, 3)--根层
2 GO
3 
4 DBCC PAGE(ALLOCATIONDB, 1, 20833, 3)--中间层
5 GO
6 
7 DBCC PAGE(ALLOCATIONDB, 1, 21098, 3)--叶子层
8 GO
复制代码

从图中我们可以看到,SQL Server在叶子层这里保存聚集键(即CustomerID列的值)。这个值是SQL Server用来指向聚集索引里对应记录的指针。手上有了这个值,SQL Server就可以在聚集索引找到对应记录——通过聚集索引查找(Clustered Index Seek)运算符。这和在堆表上定义的非聚集索引有重大区别。因为在堆表里,SQL Server使用叶子层的HEAP RID直接指向数据页里存储的对应记录。因此,SQL Server不用访问额外索引,就可以直接正确读取到数据页。

这也意味着SQL Server在堆表上通过非聚集索引找记录,比在聚集表上通过非聚集索引找记录快很多,因为SQL Server不需要执行额外的聚集索引查找(Clustered Index Seek)运算符。因此在堆表上可以读取更少的页正确找到记录。当不要高估这个细节,想想在堆表通过使用非聚集索引,性能上可以有多少好处。事实上,SQL Server总是尽量把索引页放在缓存区管理器里,因此对于SQL Server来说,使用额外的聚集索引查找(Clustered Index Seek)从聚集索引里找回记录,成本更低。

现在我们来分析下非唯一非聚集索引。先来看看根页:

1 DBCC PAGE(ALLOCATIONDB, 1, 21890, 3)
2 GO

从上图可以看出,非唯一非聚集索引根页里,SQL Server这里保存里非聚集索引键和聚集索引键,这个和刚才的唯一聚集索引根页是不一样的。

SQL Server这里需要使用唯一聚集键来使非唯一非聚集索引键唯一。这在非唯一非聚集索引的每一层都会保存,从索引根页到叶子层。这就是说你需要更多的存储空间来保存索引,因为SQL Server在每条索引记录里不仅保存唯一聚集键,也保存非唯一非聚集索引键。因此当你选择不好的聚集键(像 CHAR(100)等)时,情况会变得更糟。

复制代码
1 DBCC PAGE(ALLOCATIONDB, 1, 21890, 3)--根层
2 GO
3 
4 DBCC PAGE(ALLOCATIONDB, 1, 21889, 3)--中间层
5 GO
6 
7 DBCC PAGE(ALLOCATIONDB, 1, 22087, 3)--叶子层
8 GO
复制代码

我们换参数1再来看看根页信息:

1 DBCC TRACEON(3604)
2 DBCC PAGE(ALLOCATIONDB, 1, 21890, 1)
3 GO

这111 bytes包括:

  • 1 byte:状态位
  • n bytes:非唯一非聚集索引键——这里是CustomerName列,100 bytes
  • n bytes:唯一聚集索引键——这里是CustomerID列,4 bytes
  • 4 bytes:页ID(PageID)
  • 2 bytes:文件ID(FileID)

当你把这些字节长度汇总后,你就得到了刚才提到的111 bytes。因此在你创建非唯一非聚集索引时,就要考虑到这些额外存储,因为它会影响到你的非聚集索引的每一层。

在这个系列的下篇文章里,我们最后来看下在非唯一聚集索引上,唯一和非唯一非聚集索引的区别,请继续关注!


本文转自Woodytu博客园博客,原文链接:http://www.cnblogs.com/woodytu/p/4565906.html,如需转载请自行联系原作者

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
相关文章
|
3月前
|
存储 监控 数据库
什么是聚集索引和非聚集索引?
【8月更文挑战第3天】
1415 5
|
6月前
|
存储 索引
聚集索引与非聚集索引的区别
聚集索引与非聚集索引的区别
|
存储 数据库 索引
聚簇索引和非聚簇索引
聚簇索引和非聚簇索引
100 0
|
存储 Oracle 关系型数据库
主键索引是聚集索引还是非聚集索引
在聚簇索引中,主键索引的叶子节点存储的就是数据行本身,因此主键索引也被称为聚簇索引。在这种情况下,主键索引的物理顺序与数据行的物理顺序是一致的,这样可以提高查询性能和范围查询的效率。
133 0
|
存储 关系型数据库 MySQL
mysql索引(三)聚集索引与非聚集索引(辅助索引)
Mysql中常用的两个存储引擎innodb和mysiam的索引是不同的。 聚集索引就是以主键创建的索引 非聚集索引就是除了主键以外的索引。非聚集索引也叫做二级索引,不用纠结那么多名词,将其等价就行了。非聚集索引在建立的时候也未必是单列的,可以多个列来创建索引。
308 0
mysql索引(三)聚集索引与非聚集索引(辅助索引)
|
存储 安全 关系型数据库
Innodb,MyIsam,聚集索引和非聚集索引
1.Innodb和Myisam是什么和区别 Innodb和Myisam是两种类类型,在navicat中可以看到,也可以修改。
183 0
Innodb,MyIsam,聚集索引和非聚集索引
|
存储 关系型数据库 MySQL