SQL Server建立索引时,能够指定Unique使之成为惟一索引。“惟一”顾名思义,可是两都到底有什么区别呢?由于索引也是一种物理结构,因此仍是要从存储和结构上分析。sql
索引结构分叶级和非叶级,分析时咱们要分开来看,这个很重要。数据库
文中涉及的索引行大小计算,参考MSDN估计数据库大小索引部分。ide
1. 非惟一汇集索引和惟一汇集索引测试
建立两个测试表,各10000条整数,tb1惟一,tb2非惟一,有1000条为9999的重复值。this
Code
先查询索引的一些基本情况:spa
从上面的结果能够看到,不管是叶级仍是非叶级,非惟一汇集索引的索引行都比惟一的大一些,因此所占页也多一点。固然,由于测试数据很小,又是int,因此不明显。设计
那到底大在哪里呢?将二者的非叶级页和叶级页放在一块儿比一下就知道了。先找出页号,再用DBCC PAGE来查看。3d
经过Paul S. Randal写的存储过程sp_allocationMetadata能够查到根页和每级索引的首页。指针
就挑这两个页作对比。code
发现多出一个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
二者的页级大小是同样的,非叶级页的相差8bytes。
跟1.中的分析方法同样,挑两个非叶级页出来对比一下,看相差在哪里。
非惟一索引行多了一个HEAP RID,MSDN说明:
“若是非汇集索引不是惟一的,数据行定位符将与非汇集索引键组合使用,以便为每一行生成惟一的键值。若是非汇集索引在堆上,则数据行定位符是堆 RID。其大小是 8 个字节。”
二者叶级索引行大小=INT型4字节+无变长列+1字节行头+3字节NULL位图+8字节RID=4+0+1+3+8=16
惟一索引的非叶级行=INT型4字节+无变长列+1字节行头+3字节NULL位图+6字节子页索引=14
非惟一索引的非叶级行=INT型4字节+无变长列+1字节行头+3字节NULL位图+6字节子页索引+8字节的RID=22
小结:堆表上的非惟一索引在非叶级索引行上比惟一索引多出一列行定位符RID,而叶级是同样的,都有RID列。因此非惟一要占用更多的空间。
3.惟一汇集索引表上的惟一和非惟一非汇集索引
跟2.中的测试数据同样,只是把ID列改为汇集主键。执行:
Code
这里有意思的是汇集索引的非叶级行只有11字节,跟一样的1.中的14相差了3字节。这3字节是由于如今这个表,索引列是自增主键,是不能为NULL的,因此就没有NULL位图的3个字节的开销了。
二者的区别在于对汇集索引键的引用上,即“id”和“id(key)”。MSDN说明:
“若是非汇集索引不是惟一的,数据行定位符将与非汇集索引键组合使用,以便为每一行生成惟一的键值。若是非汇集索引在汇集索引之上,则数据行定位符是汇集键。”
惟一非汇集索引中的“id”是作为行定位符引用的,非惟一非汇集索引中的“id(key)”,不只是作为行定位符引用,而且仍是此索引自己键列(“key”的含义)。
非叶级行相差4个字节,对比一下页的内容就知道差在哪里:
从上图看出非惟一索引比惟一的多了一列”id(key)”,这是前者引用了汇集索引键并作作本身的键列,而惟一索引不须要这样。
小结:惟一汇集索引表上的非惟一非汇集索引与惟一非汇集索引,在叶级上大小是同样的,在非叶级行多引用一列“汇集键”,因此前者占用的存储空间也会更大一些。
4. 非惟一汇集索引表上的惟一和非惟一非汇集索引
测试数据中给制造了一些重复数据。
Code
由前文的分析可知上图全部索引的minSize和maxSize相差8字节都是由UNIQUEIFIER产生。
两个非汇集索引的叶级行同样大,缘由跟3. 中分析的同样,只是二者除了引用汇集键以外,还会引用跟汇集键结合使用的UNIQUEIFIER。
可是非叶级页两差有着较大的差别,查看页面:
从页面内容能够看出,非惟一非汇集比惟一多了两列,其它这两列能够当作一列,缘由就是当汇集索引不惟一时,会生成UNIQUIFIER并结合,用以保证汇集键惟一。
小结:非惟一汇集索引表上的非惟一非汇集索引在叶级行上大小是同样的,而在非叶级行上前者比后大,因此也占用更多存储空间。
总结:
1. 在表和索引设计阶段,若是可能,字段设定为不容许NULL,索引设定为惟一。这样节约存储空间并提升了IO效率。
2. 汇集索引键列应该尽可能选用窄的字段,由于非汇集索引会引用其键列。若是汇集键过大则会使非汇集索引同时也占用更多存储空间。
3. SQL Server在建立索引时,默认是建立非惟一的。因此在建立索引时,要认真考虑是否能够建立为惟一索引。
4. 文中用词有些绕。