SQL Server唯一索引和非唯一索引的区别简析
SQL Server创建索引时,可以指定Unique使之成为唯一索引。“唯一”顾名思义,但是两都到底有什么区别呢?因为索引也是一种物理结构,所以还是要从存储和结构上分析。
索引结构分叶级和非叶级,分析时我们要分开来看,这个很重要。
文中涉及的索引行大小计算,参考MSDN估计数据库大小索引部分。
1. 非唯一聚集索引和唯一聚集索引
  创建两个测试表,各10000条整数,tb1唯一,tb2非唯一,有1000条为9999的重复值。
Code
create table tb1
(col1 int);
declare @i int=1
while @i<10001
begin
insert into tb1 values(@i);
set @i=@i+1;
end;
create unique clustered index ucix on tb1 (col1)
go
-------
create table tb2
(col2 int);
declare @i int=1
while @i<9001
begin
insert into tb2 values(@i);
set @i=@i+1;
end;
go
insert into tb2 values(9999)
go 1000;
create clustered index cix on tb2 (col2)
go
先查询索引的一些基本状况:
从上面的结果可以看到,无论是叶级还是非叶级,非唯一聚集索引的索引行都比唯一的大一些,所以所占页也多一点。当然,因为测试数据很小,又是int,所以不明显。
 
那到底大在哪里呢?将两者的非叶级页和叶级页放在一起比一下就知道了。先出页号,再用DBCC PAGE来查看。
通过Paul S. Randal写的存储过程sp_allocationMetadata可以查到根页和每级索引的首页。
 
就挑这两个页做对比。
发现多出一个UNIQUIFIER,同样叶级也是一样。MSDN说明:
“如果聚集索引不是唯一的索引,SQL Server 将添加在内部生成的值(称为唯一值)以使所有重复键唯一。此四字节的值对于用户不可见。仅当需要使聚集键唯一以用于非聚集索引中时,才添加该值。”
还有UNIQUIFIER不是一个全局自增列,重复记录增加时此值会发生改变,并且它是一个可为null的变长列。
现在来算一算索引行大小:
   两个表都是只有一个int型可为NULL的字段,而聚集索引叶级是存储数据本身
  叶级是一个4字节的INT列,无变长列,加上3字节的NULL位图,再加上4字节的行头开销:两个表的叶级minSize =4+0+3+4=11
  非叶级是一个4字节的INT列,无变长列,加上3字节的NULL位图,加上1字节的行头开销,再加6字节的子页指针:两个表的非叶级minSize=4+0+3+1+6=14
  tb1的索引行大小是一致的minSize=maxSize,因为它是唯一的。tb2的索引行大小不一致,有大有小,大的索引行是因为:a)不唯一 b)UNIQUIFIER
  唯一标识列增加了2+1*2+4=8字节开销,tb2的min和max相差就是这8字节。
  tb2的叶级maxSize=4+8+3+4=19
  tb2的非叶级maxSize=4+8+3+1+6=22
小结:非唯一聚集索引为保证索引键值唯一性,会生成UNIQUIFIER与键列一起组成索引键值。同时无论在叶级还是非叶页级,都比唯一索引占用更多存储空间。
 
2.堆表上的唯一和非唯一的非聚集索引
Code
create  table IndexTest
(id int identity,
UniqueCol int,
NonuniqueCol int)
go
set nocount on;
declare @i int=1;
while @i<100000
begin
  insert into IndexTest
  values(@i,@i);
  set @i=@i+1;
end
set nocount off;
go
create unique index UIX_UniqueCol on IndexTest (UniqueCol);
create index IX_NonuniqueCol on IndexTest (NonuniqueCol);
go
select i.name,ips.index_id,ips.index_type_desc,index_depth,index_level,page_count,record_count,
min_record_size_in_bytes as minSize,max_record_size_in_bytes as maxSize,avg_record_size_in_bytes as avgSize
from sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('IndexTest'),null,null,'DE
TAILED') ips
inner join sys.indexes i
sql软件长什么样
on ips.object_id=i.object_id and ips.index_id=i.index_id
order by name,index_level
两者的页级大小是一样的,非叶级页的相差8bytes。
 
跟1.中的分析方法一样,挑两个非叶级页出来对比一下,看相差在哪里。
非唯一索引行多了一个HEAP RID,MSDN说明:
“如果非聚集索引不是唯一的,数据行定位符将与非聚集索引键组合使用,以便为每一行生成唯一的键值。如果非聚集索引在堆上,则数据行定位符是堆 RID。其大小是 8 个字节。”
两者叶级索引行大小=INT型4字节+无变长列+1字节行头+3字节NULL位图+8字节RID=4+0+1+3+8=16

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。