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

 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 unique clustered index on the previous created table
13 CREATE UNIQUE CLUSTERED INDEX idx_Customers ON Customers(CustomerID)
14 GO
15 -- Insert 80.000 records
16 DECLARE @i INT = 1
17 WHILE (@i <= 80000)
18 BEGIN
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     SET @i += 1
27 END
28 GO
29
30 -- Create a unique non clustered index on the clustered table
31 CREATE UNIQUE NONCLUSTERED INDEX idx_UniqueNCI_CustomerID
32 ON Customers(CustomerName)
33 GO
34
35 -- Create a non-unique non clustered index on the clustered table
36 CREATE NONCLUSTERED INDEX idx_NonUniqueNCI_CustomerID
37 ON Customers(CustomerName)
38 GO

在2个非聚集索引创建后,我们可以使用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,非唯一非聚集索引的记录长度是111 bytes。因此这2个索引的内部存储格式肯定不同。我们从唯一非聚集索引开始分析。

我们可以通过DBCC IND命令找出索引根页,聚集索引的INDEX ID为1,非聚集索引的INDEX ID从2开始,依次递增,这里应该是2和3。

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

从这里我们可以看出,唯一非聚集索引的根页是20834,非唯一非聚集索引的根页是21890。

我们看下唯一非聚集索引的根页内容:

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

从图中我们可以看到,每条索引记录包含非聚集键(这里是唯一的)——即CustomerName列。

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

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

这里的107 bytes包含下列信息:

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

在唯一非聚集索引里,所有非叶子层的每条索引记录都包含这107 bytes信息。因此,你的非聚集索引键大小会影响到每个索引页可以存储多少条索引记录。这样的话,这个例子的CHAR(100),并不是一个很好的索引键。

我们继续往下看,索引叶子层的存储情况:

1 DBCC PAGE(ALLOCATIONDB, 1, 20834, 3)--根层
2 GO
3
4 DBCC PAGE(ALLOCATIONDB, 1, 20833, 3)--中间层
5 GO
6
7 DBCC PAGE(ALLOCATIONDB, 1, 21098, 3)--叶子层
8 GO

从图中我们可以看到,SQL Server在叶子层这里保存聚集键(即CustomerID列的值)。这个值是SQL Server用来指向聚集索引里对应记录的指针。手上有了这个值,SQL Server就可以在聚集索引找到对应记录——通过聚集索引查找(Clustered Index Seek)运算符。这和在堆表上定义的非聚集索引有重大区别。因为在堆表里,SQL Server使用叶子层的HEAP RID直接指向数据页里存储的对应记录。因此,SQL Server不用访问额外索引,就可以直接正确读取到数据页。

这也意味着SQL Server在堆表上通过非聚集索引找记录,比在聚集表上通过非聚集索引找记录快很多,因为SQL Server不需要执行额外的聚集索引查找(Clustered Index Seek)运算符。因此在堆表上可以读取更少的页正确找到记录。当不要高估这个细节,想想在堆表通过使用非聚集索引,性能上可以有多少好处。事实上,SQL Server总是尽量把索引页放在缓存区管理器里,因此对于SQL Server来说,使用额外的聚集索引查找(Clustered Index Seek)从聚集索引里找回记录,成本更低。

现在我们来分析下非唯一非聚集索引。先来看看根页:

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

从上图可以看出,非唯一非聚集索引根页里,SQL Server这里保存里非聚集索引键和聚集索引键,这个和刚才的唯一聚集索引根页是不一样的。

SQL Server这里需要使用唯一聚集键来使非唯一非聚集索引键唯一。这在非唯一非聚集索引的每一层都会保存,从索引根页到叶子层。这就是说你需要更多的存储空间来保存索引,因为SQL Server在每条索引记录里不仅保存唯一聚集键,也保存非唯一非聚集索引键。因此当你选择不好的聚集键(像 CHAR(100)等)时,情况会变得更糟。

1 DBCC PAGE(ALLOCATIONDB, 1, 21890, 3)--根层
2 GO
3
4 DBCC PAGE(ALLOCATIONDB, 1, 21889, 3)--中间层
5 GO
6
7 DBCC PAGE(ALLOCATIONDB, 1, 22087, 3)--叶子层
8 GO

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

1 DBCC TRACEON(3604)
2 DBCC PAGE(ALLOCATIONDB, 1, 21890, 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)

当你把这些字节长度汇总后,你就得到了刚才提到的111 bytes。因此在你创建非唯一非聚集索引时,就要考虑到这些额外存储,因为它会影响到你的非聚集索引的每一层。

在这个系列的下篇文章里,我们最后来看下在非唯一聚集索引上,唯一和非唯一非聚集索引的区别,请继续关注!

参考文章:

https://www.sqlpassion.at/archive/2010/08/31/unique-and-non-unique-non-clustered-indexes-on-a-unique-clustered-index/

转载于:https://www.cnblogs.com/woodytu/p/4565906.html

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

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

    索引键的唯一性(2/4):唯一与非唯一聚集索引 原文:索引键的唯一性(2/4):唯一与非唯一聚集索引 在上一篇文章里,我们讨论了堆表上唯一/非唯一非聚集索引.在SQL Server里没有聚集索引定义的 ...

  2. 索引深入浅出(6/10):选择正确并合适的聚集索引键

    今天我们来探讨下聚集索引的设计要求.在选择聚集索引键时,有好几点需要考虑的.选择聚集索引键没有硬性规定.多实践,多从网上找问题的答案都会帮你找到正确的索引键. 唯一性(Uniqueness) SQL ...

  3. 唯一聚集索引上的唯一和非唯一非聚集索引

    在上篇文章里,我讨论了唯一和非唯一聚集索引的区别.我们已经知道,SQL Server内部使用4 bytes的uniquifier来保证非唯一聚集索引行唯一.今天我们来看下唯一聚集索引上,唯一和非唯一非 ...

  4. mysql 外键唯一,关于mysql:外键可以引用非唯一索引吗?

    我认为外键意味着单行必须引用单行,但是我正在查看某些表,但事实并非如此. 表1在表2的列2上有一个具有外键约束的列1,但是在表2中有很多记录在列2中具有相同的值. 在column2上也有非唯一索引. ...

  5. php 数组改成索引数组_PHP:用现有的唯一值替换数组索引键

    我查询了数据库,并将结果存储到数组中.看起来像这样: Array ( [0] => Array ( [Submission_ID] => 111 [First_Name] => Dy ...

  6. 聚簇索引、非聚簇索引、普通索引、唯一索引

    一.聚簇索引 VS 非聚簇索引 聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据 非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam通过key_buf ...

  7. sql加上唯一索引后批量插入_MySQL当批量插入遇上唯一索引

    一.背景 以前使用SQL Server进行表分区的时候就碰到很多关于唯一索引的问题:Step8:SQL Server 当表分区遇上唯一约束,没想到在MySQL的分区中一样会遇到这样的问题:MySQL表 ...

  8. 数据库索引键uk_数据库SQLServer

    2015.5.20 数据库 类型:层次型,网状型,关系型(目前应用最广的数据库类型) 常见数据库:SQLServer,Oracle,DB2,Access,VisualFoxpro,MySQL(目前应用 ...

  9. MySQL中的聚簇索引、非聚簇索引、联合索引和唯一索引

    一.索引类型 索引根据底层实现可分为B-Tree索引和哈希索引,大部分时候我们使用的都是B-Tree索引,因为它良好的性能和特性更适合于构建高并发系统. 根据索引的存储方式来划分,索引可以分为聚簇索引 ...

最新文章

  1. 09 Storage Structure and Relationships
  2. 将Centos的yum源更换为国内的阿里云源
  3. onCompositionStart与compositionend
  4. Vivado安装器件不全
  5. Jenkins Pipeline动态使用Git分支名称的技巧
  6. java挖矿算法_Scrypt 不止是加密算法,也是莱特币的挖矿算法
  7. 手把手演示:如何规划一个企业级数据中台
  8. bzoj 3055礼物运送 floyed + 状压DP
  9. pdf阅读器改背景色
  10. AxWebBrowser打开本地文件
  11. html界面左侧没有顶格,求解,html页面顶部有空白??
  12. mysql导出数据dump_MYSQL使用mysqldump导出表的部分数据
  13. html5画布作品,10个会让你惊叹不已的HTML5画布(CANVAS)技术应用演
  14. java 执行查询_在单个语句中用Java执行多个查询
  15. 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。
  16. Spring No mapping found for HTTP request with URI错误
  17. PCIe协议在具体实现时的大体框图
  18. 关于 HenCoder
  19. 西威变频器avo下载调试资料_免费下载 |《西门子全集成自动化技术》,很全很详细...
  20. 实验室设备管理系统的设计与实现程序设计与实现

热门文章

  1. GDUT1174:我是好人4(容斥定理)
  2. paramiko在windows上的安装和使用
  3. windows消息定义
  4. 利用windows优化大师软件卸载一手和清理一招
  5. index 把@前的截断作为
  6. 【Node】node启动过程
  7. html5 head 标签
  8. Git新建分支出现fatal: Not a valid object name: ‘master‘错误
  9. (二)git常用基本概念
  10. DSG-Oracle数据库在线迁移服务