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

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

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

下列代码再次创建我们的Customers表,这次在它上面定义唯一聚集索引,最后定义2个聚集索引,1个是唯一的,另1个是非唯一的。

 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 non unique clustered index on the previous created table
13 CREATE CLUSTERED INDEX idx_Customers ON Customers(CustomerID)
14 GO
15
16 -- Insert 80.000 records
17 DECLARE @i INT = 1
18 WHILE (@i <= 20000)
19 BEGIN
20     DECLARE    @j INT = 1
21     INSERT INTO Customers VALUES
22     (
23         @i,
24         'CustomerName' + CAST(@i AS CHAR) + CAST(@j AS CHAR),
25         'CustomerAddress' + CAST(@i AS CHAR),
26         'Comments' + CAST(@i AS CHAR)
27     )
28
29     SET @j += 1;
30
31     INSERT INTO Customers VALUES
32     (
33         @i,
34         'CustomerName' + CAST(@i AS CHAR) + CAST(@j AS CHAR),
35         'CustomerAddress' + CAST(@i AS CHAR),
36         'Comments' + CAST(@i AS CHAR)
37     )
38
39     SET @j += 1;
40
41     INSERT INTO Customers VALUES
42     (
43         @i,
44         'CustomerName' + CAST(@i AS CHAR) + CAST(@j AS CHAR),
45         'CustomerAddress' + CAST(@i AS CHAR),
46         'Comments' + CAST(@i AS CHAR)
47     )
48
49     SET @j += 1;
50
51     INSERT INTO Customers VALUES
52     (
53         @i,
54         'CustomerName' + CAST(@i AS CHAR) + CAST(@j AS CHAR),
55         'CustomerAddress' + CAST(@i AS CHAR),
56         'Comments' + CAST(@i AS CHAR)
57     )
58
59     SET @i += 1
60 END
61 GO
62
63 -- Create a unique non clustered index on the clustered table
64 CREATE UNIQUE NONCLUSTERED INDEX idx_UniqueNCI_CustomerID
65 ON Customers(CustomerName)
66 GO
67
68 -- Create a non-unique non clustered index on the clustered table
69 CREATE NONCLUSTERED INDEX idx_NonUniqueNCI_CustomerID
70 ON Customers(CustomerName)
71 GO

我们通过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,而唯一聚集索引每个索引行平均占用117 bytes(最小111 bytes,最大119 bytes)。我们用DBCC PAGE分析下各自的根页。

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

首先是唯一聚集索引,它的根页是21058。

1 DBCC PAGE(ALLOCATIONDB, 1, 21058, 3)
2 GO
3
4 DBCC PAGE(ALLOCATIONDB, 1,21057,3)
5 GO

从图中我们可以看出,唯一聚集索引在索引根层(还有中间层)只保存了唯一聚集键,因为聚集键本身就已经唯一了。

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

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

这107 bytes包含下列信息:

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

我们再来看看唯一聚集索引的叶子页。

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

我们可以看出SQL Server通过唯一聚集键和uniquifier来指向聚集表的对应记录。

这里我们可以得出结论:在唯一聚集索引上的唯一聚集索引只在叶子层使用4 bytes的uniquifier,因为这里SQL Server使用聚集键和uniquifier直接指向对应的记录。这个4 bytes的uniquifier在唯一聚集索引的非叶子层(根层和中间层)不存在。

我们再来看看唯一聚集索引的根页,它的根页是22986。

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

这里的根页输出信息非常有意思!索引记录的键必须设计为唯一。SQL Server如何让唯一聚集索引键唯一呢?非常简单——加下聚集键(CustomerID (key))(4 bytes)。但是聚集键这里默认还是不唯一的,因此SQL Server又加了uniquifier(4 bytes),因次当你uniquifier不为0的时候,每个索引行都有8 bytes的开销。当uniquifier为0时,你只要4 bytes的开销,因为这个情况下uniquifier并不物理保存在索引记录里,0是SQL Server自动假定的值。

我们再看看参数为1的信息:

1 DBCC TRACEON(3604)
2 DBCC PAGE(ALLOCATIONDB, 1, 22986, 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)
  • 4 bytes:uniquifier用到的字节
  • 4 bytes:uniquifier本身值,如果uniquifier非0的话

刚才我们通过sys.dm_db_index_physical_stats知道唯一聚集索引的索引记录是111 bytes,最长是117 bytes。

我们来看看唯一聚集索引的叶子页:

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

这和唯一聚集索引上定义的唯一聚集索引是一样的。叶子层通过聚集键(CustomerID)和uniquifier指向聚集表的对应记录。从这个例子我们可以看出,当你在唯一聚集索引上定义唯一聚集索引时,会有巨大的开销(每个索引行 8 bytes),因为SQL Server内部要保证聚集键唯一,这就需要大量的存储开销。  

时间: 2024-09-20 00:26:02

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

索引键的唯一性(1/4):堆表上的唯一与非唯一非聚集索引的区别

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

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

原文:索引键的唯一性(2/4):唯一与非唯一聚集索引 在上一篇文章里,我们讨论了堆表上唯一/非唯一非聚集索引.在SQL Server里没有聚集索引定义的叫堆表.当你在堆表上定义了一个聚集索引,你的表数据就会重组按聚集键的顺序进行物理存储,因为这个表叫做聚集表.这篇文章里,我想谈下唯一和非唯一聚集索引之间的区别,这2类聚集索引对存储的影响. 看这个文章之前,希望你对聚集索引有个基本的认识,并且知道堆表和聚集表之间的区别,还有当在表上定义了一个聚集索引,表里数据页是如何组织的(B树结构). 我们从唯

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

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

第十二章——SQLServer统计信息(2)——非索引键上统计信息的影响

原文:第十二章--SQLServer统计信息(2)--非索引键上统计信息的影响 前言:         索引对性能方面总是扮演着一个重要的角色,实际上,查询优化器首先检查谓词上的统计信息,然后才决定用什么索引.一般情况下,默认会在创建索引时,索引列上均创建统计信息.但是不代表在非索引键上的统计信息对性能没有用.         如果表上的所有列都有索引,那么将会是数据库负担不起,同时也不是一个好想法,包括谓词中用到的所有列加索引同样也不是好方法.因为索引会带来负载.因为需要空间存放索引,且每个D

[20160711]索引键值在Btree索引块中的顺序3

[20160711]索引键值在B tree索引块中的顺序3.txt --上午测试索引键值在B tree索引块中的顺序,许多人认为是有序,主要是插入后再建立索引. --这样看到索引块里面的键值就是有序的. --今天测试一下,如果索引分裂后是否会排序呢?索引分裂有两种情况,前面测试leaf node 50-50 splits的情况. --继续测试leaf node 90-10 splits的情况. 测试看看. 1.环境: SCOTT@test01p> @ ver1 PORT_STRING      

[20160711索引键值在B tree索引块中的顺序2

[20160711]索引键值在B tree索引块中的顺序2.txt --上午测试索引键值在B tree索引块中的顺序,许多人认为是有序,主要是插入后再建立索引. --这样看到索引块里面的键值就是有序的. --今天测试一下,如果索引分裂后是否会排序呢?索引分裂有两种情况,先测试leaf node 50-50 splits的情况. 测试看看. 1.环境: SCOTT@test01p> @ ver1 PORT_STRING                    VERSION        BANNE

[20111223]索引键值在B tree索引块中的顺序.txt

[20111223]索引键值在B tree索引块中的顺序.txt 参考链接:http://www.adellera.it/blog/2009/05/24/order-keys-inside-index-blocks/ 自己为了加强理解重复一下对方的测试! 1.建立测试表以及索引 SQL> select * from v$version; BANNER -------------------------------------------------------------------------

阿里云推出云上首个支持广电级非编NAS产品 4K编辑无卡顿

5月23日,阿里云在2017云栖大会·成都峰会上正式推出了云上首个支持广电级非编的文件存储产品------NAS Plus,作为阿里云文件存储NAS的升级款,NAS Plus提供高达200Gbps的吞吐性能和低至1毫秒的超低延迟,支持12层4K媒体编辑能力,比一般多媒体处理能力高10倍,用户无需对现有应用做任何修改,即可应用于广电非编.基因计算.视频渲染等多种业务场景,相比同类解决方案成本下降70%.   当下视频有两个发展趋势:短视频和4K高清视频,前者快速.简明,后者时尚.精美.随着电视产业

javascript-js非IE内核浏览器上获取系统信息,包括CPU使用率。求解

问题描述 js非IE内核浏览器上获取系统信息,包括CPU使用率.求解 做Flash player,需要获取系统信息,想用js获取然后传递给AS3.js调用WMI,在非IE浏览器上没反应 解决方案 其他浏览器需要通过插件,比如FIREFOX Chrome都支持插件,然后通过插件来获取系统的CPU等信息