博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
索引键的唯一性(2/4):唯一与非唯一聚集索引
阅读量:6548 次
发布时间:2019-06-24

本文共 4304 字,大约阅读时间需要 14 分钟。

原文:

在上一篇文章里,我们讨论了。在SQL Server里没有聚集索引定义的叫堆表。当你在堆表上定义了一个聚集索引,你的表数据就会重组按聚集键的顺序进行物理存储,因为这个表叫做聚集表。这篇文章里,我想谈下唯一和非唯一聚集索引之间的区别,这2类聚集索引对存储的影响。

看这个文章之前,希望你对聚集索引有个基本的认识,并且知道堆表和聚集表之间的区别,还有当在表上定义了一个聚集索引,表里数据页是如何组织的(B树结构)。

我们从唯一聚集索引谈起。在SQL Server里你有很多方法去定义唯一聚集索引。第1个最简单的方法就是列上定义一个主键(PRIMARY KEY)约束。SQL Server通过在表上创建那列的唯一聚集索引来施行主键(PRIMARY KEY)约束。另外一个方法是通过CREATE CLUSTERED INDEX语句来常见唯一聚集索引——但当你不指定UNIQUE属性时,SQL Server默认是会为你创建非唯一的聚集索引!下列这段代码会创建Customers表,这个表结构和一样,但这次我们在CustomerID列创建主键(PRIMARY KEY)约束。因此SQL Server会在表上创建唯一聚集索引,在叶子层里,数据页是按CustomerID列值排序的。

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 PRIMARY KEY IDENTITY(1, 1), 6    CustomerName CHAR(100) NOT NULL, 7    CustomerAddress CHAR(100) NOT NULL, 8    Comments CHAR(189) NOT NULL 9 )10 GO11 12 -- Insert 80.000 records13 DECLARE @i INT = 114 WHILE (@i <= 80000)15 BEGIN16    INSERT INTO Customers VALUES17    (18       'CustomerName' + CAST(@i AS CHAR),19       'CustomerAddress' + CAST(@i AS CHAR),20       'Comments' + CAST(@i AS CHAR)21    )22 23    SET @i += 124 END25 GO

我们可以通过DBCC IND命令找出索引根页后(PageType为2,IndexLevel为2,即B树有3层:根和叶子层,PagePID为15359),就可以使用DBCC PAGE查看根页的内容。这里我的索引根页是15359。

1 TRUNCATE TABLE dbo.sp_table_pages2 INSERT INTO dbo.sp_table_pages3 EXEC('DBCC IND(ALLOCATIONDB, Customers, -1)') 4 5 SELECT * FROM dbo.sp_table_pages ORDER BY IndexLevel DESC

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

从上图里,我们可以看到每个索引记录包含聚集键,在这个例子是CustomerID列的值。

如果你从字节存储级别分析聚集索引记录的话,你会发现SQL Server这里使用下列字节信息:

  • 1 byte:状态位
  • n bytes:聚集键——这个例子里是4 bytes
  • 4 bytes:页ID(PageID)
  • 2 bytes:文件ID(FileID)

可以看出,聚集键的长度直接影响索引记录的长度。这就是说,你的聚集键长度越小,索引页上就可以存放更多的索引记录,因此你的聚集索引将更紧凑,查找更快,维护更容易。当你在你的聚集索引继续往下看时,你会发现所有中间层的索引结构和刚才的描述完全一样。这2层是没有任何区别的,除了索引叶子层,因为这层包含你实际逻辑排序的数据页。

现在我们来看看SQL Server里非唯一聚集索引,看看它们和唯一聚集索引的区别。为了演示这类索引,我重建了Customers表,并通过CREATE CLUSTERED INDEX语句在表上创建了非唯一聚集索引。

1 DROP TABLE dbo.Customers 2 -- Create a table with 393 length + 7 bytes overhead = 400 bytes 3 -- Therefore 20 records can be stored on one page (8.096 / 400) = 20,24 4 CREATE TABLE Customers 5 (  6    CustomerID INT NOT NULL, 7    CustomerName CHAR(100) NOT NULL, 8    CustomerAddress CHAR(100) NOT NULL, 9    Comments CHAR(181) NOT NULL10 )11 GO12 13 -- Create a non unique clustered index14 CREATE CLUSTERED INDEX idx_Customers_CustomerID15 ON Customers(CustomerID)16 GO

最后,我插入80000条记录,这些记录的CustomerID列(聚集键)不再唯一:

1 -- Insert 80.000 records 2 DECLARE @i INT = 1 3 WHILE (@i <= 20000) 4 BEGIN 5    INSERT INTO Customers VALUES 6    ( 7       @i, 8       'CustomerName' + CAST(@i AS CHAR), 9       'CustomerAddress' + CAST(@i AS CHAR),10       'Comments' + CAST(@i AS CHAR)11    )12    INSERT INTO Customers VALUES13    (14       @i,15       'CustomerName' + CAST(@i AS CHAR),16       'CustomerAddress' + CAST(@i AS CHAR),17       'Comments' + CAST(@i AS CHAR)18    )19    INSERT INTO Customers VALUES20    (21       @i,22       'CustomerName' + CAST(@i AS CHAR),23       'CustomerAddress' + CAST(@i AS CHAR),24       'Comments' + CAST(@i AS CHAR)25    )26  27    INSERT INTO Customers VALUES28    (29       @i,30       'CustomerName' + CAST(@i AS CHAR),31       'CustomerAddress' + CAST(@i AS CHAR),32       'Comments' + CAST(@i AS CHAR)33    )34 35 SET @i += 136 END37 GO

我们找下这个非唯一聚集索引的根页:

1 TRUNCATE TABLE dbo.sp_table_pages2 INSERT INTO dbo.sp_table_pages3 EXEC('DBCC IND(ALLOCATIONDB, Customers, -1)') 4 5 SELECT * FROM dbo.sp_table_pages ORDER BY IndexLevel DESC

我们再来看看根页的内容:

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

我们发现,SQL Server这里增加了UNIQUIFIER (key)的额外列。这列是SQL Server用来保证非唯一聚集键唯一。UNIQUIFIER (key)是4 bytes始于0的长整型值。当你有2条CustomerID值都是1380时,第1条的UNIQUIFIER为0,第2条的UNIQUIFIER值为1。但SQL Server只在索引的导航结构(高于叶子层的所有层)里保存UNIQUIFIER,即叶子层的UNIQUIFIER不为0。SQL Server只在非唯一聚集索引的导航结构里包含0值的UNIQUIFIER,这就是说导航结构里是不物理保存UNIQUIFIER的。在非唯一聚集索引里,唯一保存UNIQUIFIER的地方是在数据页,就是保存实际数据的地方。下图是我们聚集聚集索引里的中间层,你会看到UNIQUIFIER在这里是保存的。

1 DBCC PAGE(ALLOCATIONDB, 1, 15359, 3)2 GO3 4 DBCC PAGE(ALLOCATIONDB, 1, 14635, 3)5 GO

最后我们看看数据页14633:

1 DBCC TRACEON(3604)2 DBCC PAGE(ALLOCATIONDB, 1, 14633, 3) with tableresults3 GO

我们来找4条CustomerID值为1的记录,看看UNIQUIFIER的值是多少(应该是0,1,2,3)。

因此唯一和非唯一聚集索引的区别是在数据页,因为当使用非唯一聚集索引时,SQL Server使用4 bytes长的UNIQUIFIER来保证它们唯一,要记住,在你定义非唯一聚集索引时,这个额外开销始终存在。

下面文章我们会详细分析下。请继续关注! 

转载地址:http://ttgdo.baihongyu.com/

你可能感兴趣的文章
一个listener.ora配置细节的问题
查看>>
[转载]webarchive文件转换成htm文件
查看>>
家里蹲大学数学杂志期刊模式目录
查看>>
什么是 stack?- 每天5分钟玩转 Docker 容器技术(111)
查看>>
ConcurrentDictionary线程不安全么,你难道没疑惑,你难道弄懂了么?
查看>>
C# 利用BarcodeLib.dll生成条形码(一维,zxing,QrCodeNet/dll二维码)
查看>>
linux诡异的硬盘不足
查看>>
定时任务发展史(一)
查看>>
C语言 第六章 多重循环练习
查看>>
Attribute2Image --- Conditional Image Generation from Visual Attributes 论文笔记
查看>>
Linux卸载MySQL
查看>>
使用ash分析ORA-01652问题
查看>>
MongoDB 稀疏(间隙)索引(Sparse Indexes)
查看>>
windows 端口转发自带工具 配合n2n用(该工具在本地转发的端口只能在本地访问 ,这一点太不爽了,还是用https://boutell.com/rinetd/方便些)...
查看>>
linux命令之useradd
查看>>
gradle中使用cobertura做代码覆盖(转)
查看>>
java中文排序问题(转)
查看>>
mysql读注意事项
查看>>
PHP内核探索之变量(1)Zval
查看>>
SQL 2012 镜像 图解(解决1418)
查看>>