索引键的唯一性(2/4):唯一与非唯一聚集索引

在上一篇文章里,我们讨论了堆表上唯一/非唯一非聚集索引。在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 GO
11
12 -- Insert 80.000 records
13 DECLARE @i INT = 1
14 WHILE (@i <= 80000)
15 BEGIN
16    INSERT INTO Customers VALUES
17    (
18       ‘CustomerName‘ + CAST(@i AS CHAR),
19       ‘CustomerAddress‘ + CAST(@i AS CHAR),
20       ‘Comments‘ + CAST(@i AS CHAR)
21    )
22
23    SET @i += 1
24 END
25 GO

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

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

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 NULL
10 )
11 GO
12
13 -- Create a non unique clustered index
14 CREATE CLUSTERED INDEX idx_Customers_CustomerID
15 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 VALUES
13    (
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 VALUES
20    (
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 VALUES
28    (
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 += 1
36 END
37 GO

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

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

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

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 GO
3
4 DBCC PAGE(ALLOCATIONDB, 1, 14635, 3)
5 GO

最后我们看看数据页14633:

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

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

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

下面文章我们会详细分析下唯一聚集索引上,唯一和非唯一非聚集索引的区别。请继续关注!

时间: 2024-10-02 20:30:48

索引键的唯一性(2/4):唯一与非唯一聚集索引的相关文章

索引键的唯一性(4/4):非唯一聚集索引上的唯一和非唯一非聚集索引

在上一篇文章里,我谈了唯一聚集索引上的唯一和非唯一非聚集索引的区别.在这篇文章里,我想谈下非唯一聚集索引上的唯一和非唯一聚集索引的区别.我们都知道,SQL Server内部把非唯一聚集索引当作唯一聚集索引处理的.如果你定义了一个非唯一聚集索引,SQL Server会增加叫做uniquifier到你的索引记录,它导致你聚集索引的导航结构(B树的非叶子层)里,每条索引行都要用到4 bytes的开销. 下列代码再次创建我们的Customers表,这次在它上面定义非唯一聚集索引,最后定义2个非聚集索引,

堆表上的唯一与非唯一非聚集索引的区别

在这篇文章里,我想详细介绍下SQL Server里唯一与非唯一非聚集索引的区别.看这个文章前,希望你已经理解了聚集和非聚集索引的概念,还有在SQL Server里是如何使用的. 很多人对唯一和非唯一索引非聚集索引的认识都不是很清晰.事实上,SQL Server在存储上这2类索引有着本质的区别,这些区别会影响到索引占用空间的大小和索引的使用效率. 今天我们从SQL Server里的堆表(Heap table) ,它是没有聚集索引定义的表,在它建立唯一和非唯一非聚集索引,来开始我们的分析.下列脚本会

主键与聚集索引的区别

有些人可能对主键和聚集索引有所混淆,其实这两个是不同的概念,下面是一个简单的描述.不想看绕口文字者,直接看两者的对比表.尤其是最后一项的比较. 主键(PRIMARY KEY ) 来自MSDN的描述: 表通常具有包含唯一标识表中每一行的值的一列或一组列.这样的一列或多列称为表的主键 (PK),用于强制表的实体完整性.在创建或修改表时,您可以通过定义 PRIMARY KEY 约束来创建主键. 一个表只能有一个 PRIMARY KEY 约束,并且 PRIMARY KEY 约束中的列不能接受空值.由于

搜索引擎算法研究专题三:聚集索引与非聚集索引介绍

搜索引擎算法研究专题三:聚集索引与非聚集索引介绍 聚集索引介绍 在聚集索引中,表中各行的物理顺序与键值的逻辑(索引)顺序相同.表只能包含一个聚集索引. 如果不是聚集索引,表中各行的物理顺序与键值的逻辑顺序不匹配.聚集索引比非聚集索引有更快的数据访问速度. 聚集索引通常可加快 UPDATE 和 DELETE 操作的速度,因为这两个操作需要读取大量的数据.创建或修改聚集索引可能要花很长时间,因为执行这两个操作时要在磁盘上对表的行进行重组. 可考虑将聚集索引用于: 1.包含数量有限的唯一值的列,如 s

索引深入浅出:非聚集索引的B树结构在聚集表

一个表只能有一个聚集索引,数据行以此聚集索引的顺序进行存储,一个表却能有多个非聚集索引.我们已经讨论了聚集索引的结构,这篇我们会看下非聚集索引结构. 非聚集索引的逻辑呈现 简单来说,非聚集索引是表的子集.当我们定义了一个非聚集索引时,SQL Server把整套非聚集索引键存在不同的页里.我们来看下一个包含BusinessEntityID(PK),PersonType,FirstName,LastName这4列的表,这个表上有一个非聚集索引定义.主体表按BusinessEntityID列(聚集索引

聚集索引: 三级阶梯SQL Server索引

原文链接:http://www.sqlservercentral.com/articles/Stairway+Series/72351/   聚集索引:三级阶梯SQL Server索引 通过大卫·杜兰特,2013/01/25(第一次出版:2011/06/22) 该系列 本文是楼梯系列的一部分:SQL Server的阶梯索引 索引数据库设计的基础,告诉开发人员使用数据库设计者的意图. 不幸的是索引时往往是后加上的性能问题出现. 终于在这里是一个简单的系列文章,应该让任何数据库专业迅速"加速&quo

主键,唯一索引 聚集索引的关系

为列创建索引实际上就是为列进行排序,以方便查询.建立一个列的索引,就相当与建立一个列的排序. 主键是唯一的,所以创建了一个主键的同时,也就这个字段创建了一个唯一的索引, 唯一索引实际上就是要求指定的列中所有的数据必须不同. 主键一唯一索引的区别: 1 一个表的主键只能有一个,而唯一索引可以建多个.         2 主键可以作为其它表的外键.         3 主键不可为null,唯一索引可以为null. 聚集索引:将表内的数据按照一定的规则进行排列的目录.正因为如此,一个表中的聚焦索引只有

SQL存储原理及聚集索引、非聚集索引、唯一索引、主键约束的关系(补)

索引类型 1.          唯一索引:唯一索引不允许两行具有相同的索引值 2.          主键索引:为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型.主键索引要求主键中的每个值是唯一的,并且不能为空 3.          聚集索引(Clustered):表中各行的物理顺序与键值的逻辑(索引)顺序相同,每个表只能有一个 4.          非聚集索引(Non-clustered):非聚集索引指定表的逻辑顺序.数据存储在一个位置,索引存储在另一个位置,索引中包含指

SQL有三个类型的索引,唯一索引 不能有重复,但聚集索引,非聚集索引可以有重复

重要: (1) SQL如果创建时候,不指定类型那么默认是非聚集索引 (2) 聚集索引和非聚集索引都可以有重复记录,唯一索引不能有重复记录. (3) 主键 默认是加了唯一约束的聚集索引,但是也可以在主键创建时,指定为唯一约束的非聚集索引,因此主键仅仅是默认加了唯一约束的聚集索引,不能说主键就是加了唯一约束的聚集索引 有点拗口,可以参考我的博客:主键就是聚集索引吗? 为列创建索引实际上就是为列进行排序,以方便查询.建立一个列的索引,就相当与建立一个列的排序. 主键是唯一的,所以创建了一个主键的同时,