在“索引深入浅出:非聚集索引的B树结构在聚集表”里,我们讨论了在聚集表上的非聚集索引,这篇文章我们讨论下在堆表上的非聚集索引。

非聚集索引可以在聚集表或堆表上创建。当我们在聚集表上创建非聚集索引时,聚集索引键担当为行指针。在堆表里,文件号,页号和槽号(file id , page number and slot number)的组合在非聚集索引里担当为行指针。

我们来看下手头的一个例子。我们创建salesorderdetail表的副本,并在上面的productid和salesorderid 列创建创建非聚集索引。

 DROP TABLE SalesOrderDetailHeap

 SELECT * INTO dbo.SalesOrderDetailHeap FROM AdventureWorks2008r2.Sales.SalesOrderDetail
GO
CREATE UNIQUE INDEX Ix_ProductId ON SalesOrderDetailHeap(ProductId,Salesorderid)

收集非聚集索引相关信息:

 TRUNCATE TABLE dbo.sp_table_pages
INSERT INTO sp_table_pages EXEC('DBCC IND(IndexDB,SalesOrderDetailHeap,2)')
GO SELECT * FROM dbo.sp_table_pages ORDER BY IndexLevel DESC --根节点/索引页
DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,3720,3) DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,3608,3)--叶子节点/索引页 DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,3908,3)--叶子节点/索引页
SELECT * FROM dbo.sp_table_pages WHERE IndexLevel=0 --叶子节点/索引页

根据上述信息进行非聚集索引逻辑示意图的绘制:

现在我们来分析下SQL Server如何存储堆表的非聚集索引,首先我们通过DBCC IND命令查看非聚集索引的页分配情况,最后一个参数,2是Ix_ProductId的索引号。

 DBCC ind('IndexDB','SalesOrderDetailHeap',2)

一共返回298条记录,包括1个IAM页,288个索引页,我们用下列语句找下根层的页号:

 SELECT * FROM dbo.sp_table_pages ORDER BY IndexLevel DESC 

可以看到,indexlevel列最大值1的页号是3270,这个页就是根页,因为indexlevel列最大值是1,所以这个堆表的非聚集索引的B树结构只有2层,即根层和叶子层,也就是说288个索引页中,1个页是根层的根页(也是索引页),287个页是叶子层的索引页。我们来看看3270页的信息。

 DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,3720,3)

输出结果,和聚集表里的非聚集索引的根页结构是一样的。

我们来看看叶子层的3608页。

 DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,3608,3)--叶子节点/索引页

在聚集表的非聚集索引的叶子层,聚集键与非聚集键一齐加入了叶子层的页。这里我们没有聚集索引,索引SQL Server加了个行标识号(8 bytes大小),由文件号(2 bytes),页号(4 bytes)和槽号(2 bytes)组合而成。

从上图我们可以清楚看出,productid值为707,salesorderid值为43665的记录完整信息,可以在HeapRID 0xB800000001003E00位置找到。下面的查询可以帮我们把RID转为文件号:页号:槽号(FileId:PageId:SlotNo)格式。

 DECLARE @HeapRid BINARY(8)
SET @HeapRid = 0xB800000001003E00
SELECT
CONVERT (VARCHAR(5),
CONVERT(INT, SUBSTRING(@HeapRid, 6, 1)
+ SUBSTRING(@HeapRid, 5, 1)))
+ ':'
+ CONVERT(VARCHAR(10),
CONVERT(INT, SUBSTRING(@HeapRid, 4, 1)
+ SUBSTRING(@HeapRid, 3, 1)
+ SUBSTRING(@HeapRid, 2, 1)
+ SUBSTRING(@HeapRid, 1, 1)))
+ ':'
+ CONVERT(VARCHAR(5),
CONVERT(INT, SUBSTRING(@HeapRid, 8, 1)
+ SUBSTRING(@HeapRid, 7, 1)))
AS 'Fileid:Pageid:Slot'

1:184:62表示文件号:1 ,页号:184 ,槽号:62。我们来看看184页。

 DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,184,3)

从输出我们可以看到,productid值为707,salesorderid值为43665的记录所有列可以在槽号62找到,与1:184:62表示文件号:1 ,页号:184 ,槽号:62完全一致。

我们通过下面的查询看看SQL Server如何使用非聚集索引查找堆表上的数据,点击工具栏的显示包含实际的执行计划。

 SET STATISTICS IO ON
GO
SELECT * FROM SalesOrderDetailHeap WHERE productid=707 AND SalesOrderid=43665

SQL Server需要进行2次I/O操作到达非聚集索引的叶子层,1次I/O操作通过使用RID查找(堆)拿到剩下的数据。执行计划如下所示:

即使我们将查询语句修改为,只要 ProductId,SalesOrderid,SalesorderDetailId 这3列,SQL Server还是要进行键查找(Key lookup)操作。

 SET STATISTICS IO ON
GO
SELECT * FROM SalesOrderDetailHeap WHERE productid=707 AND SalesOrderid=43665 SET STATISTICS IO ON
GO
SELECT ProductId,SalesOrderid,SalesOrderDetailID FROM SalesOrderDetailHeap WHERE productid=707 AND SalesOrderid=43665

这是因为,SalesorderDetailId列没有定义为聚集键,在非聚集索引的叶子层没有这列。为了避免键查找(key lookup)操作,我们需要将列限制到只有非聚集索引键(ProductKey ,salesorderid)。

 SET STATISTICS IO ON
GO
SELECT ProductId,SalesOrderid FROM SalesOrderDetailHeap WHERE productid=707 AND SalesOrderid=43665

如上图所示,只有非聚集索引查找操作,没有键查找(Key lookup)操作了。

参考文章:

http://www.sqlservercentral.com/blogs/practicalsqldba/2013/03/17/sql-server-part-5-explaining-non-clustered-index-on-heap/

索引深入浅出(5/10):非聚集索引的B树结构在堆表的更多相关文章

  1. 索引深入浅出(3/10):聚集索引的B树结构

    在SQL Server里,有2种表是以存储为基础的.有聚集索引的表叫聚集表,没有聚集索引的表叫堆表.在上一篇文章,我们讨论了堆表的特性和存储结构.在这篇文章里,我们来看下聚集表. 有聚集索引的表叫聚集 ...

  2. SQL Server中的聚集索引(clustered index) 和 非聚集索引 (non-clustered index)

    本文转载自  http://blog.csdn.net/ak913/article/details/8026743 面试时经常问到的问题: 1. 什么是聚合索引(clustered index) / ...

  3. 索引深入浅出(4/10):非聚集索引的B树结构在聚集表

    一个表只能有一个聚集索引,数据行以此聚集索引的顺序进行存储,一个表却能有多个非聚集索引.我们已经讨论了聚集索引的结构,这篇我们会看下非聚集索引结构. 非聚集索引的逻辑呈现 简单来说,非聚集索引是表的子 ...

  4. InnoDB 聚集索引和非聚集索引、覆盖索引、回表、索引下推简述

    关于InnoDB 存储引擎的有聚集索引和非聚集索引,覆盖索引,回表,索引下推等概念,这些知识点比较多,也比较零碎,但是概念都是基于索引建立的,本文从索引查找数据讲述上述概念. 聚集索引和非聚集索引 在 ...

  5. SQLSERVER聚集索引与非聚集索引的再次研究(上)

    SQLSERVER聚集索引与非聚集索引的再次研究(上) 上篇主要说聚集索引 下篇的地址:SQLSERVER聚集索引与非聚集索引的再次研究(下) 由于本人还是SQLSERVER菜鸟一枚,加上一些实验的逻 ...

  6. SQLSERVER聚集索引与非聚集索引的再次研究(下)

    SQLSERVER聚集索引与非聚集索引的再次研究(下) 上篇主要说了聚集索引和简单介绍了一下非聚集索引,相信大家一定对聚集索引和非聚集索引开始有一点了解了. 这篇文章只是作为参考,里面的观点不一定正确 ...

  7. SQL Server临界点游戏——为什么非聚集索引被忽略!

    当我们进行SQL Server问题处理的时候,有时候会发现一个很有意思的现象:SQL Server完全忽略现有定义好的非聚集索引,直接使用表扫描来获取数据.我们来看看下面的表和索引定义: CREATE ...

  8. SQL Server索引 (原理、存储)聚集索引、非聚集索引、堆 <第一篇>

    一.存储结构 在SQL Server中,有许多不同的可用排列规则选项. 二进制:按字符的数字表示形式排序(ASCII码中,用数字32表示空格,用68表示字母"D").因为所有内容都 ...

  9. SQLServer之创建唯一非聚集索引

    创建唯一非聚集索引典型实现 唯一索引可通过以下方式实现: PRIMARY KEY 或 UNIQUE 约束 在创建 PRIMARY KEY 约束时,如果不存在该表的聚集索引且未指定唯一非聚集索引,则将自 ...

  10. 浅谈sql server聚集索引与非聚集索引

    今天同事的服务程序在执行批量插入数据操作时,会超时失败,代码debug了几遍一点问题都没有,SQL单条插入也可以正常录入数据,调试了一上午还是很迷茫,场面一度很尴尬,最后还是发现了问题的根本,原来是另 ...

随机推荐

  1. Git 的origin和master分析

    首先要明确一点,对git的操作是围绕3个大的步骤来展开的(其实几乎所有的SCM都是这样) 1.     从git取数据(git clone) 2.     改动代码 3.     将改动传回git(g ...

  2. [leetcode 37]sudoku solver

    1 题目: 根据给出的数独,全部填出来 2 思路: 为了做出来,我自己人工做了一遍题目给的数独.思路是看要填的数字横.竖.子是否已经有1-9的数字,有就剔除一个,最后剩下一个的话,就填上.一遍一遍的循 ...

  3. DOM扩展札记

    Selector API HTML5 DOM扩展 Element Traversal规范 Selector API 众多JavaScript库中,最常用的一个功能就是根据css选择符选择与某个模式匹配 ...

  4. Javascript事件模型系列(一)事件及事件的三种模型

    一.开篇 在学习javascript之初,就在网上看过不少介绍javascript事件的文章,毕竟是js基础中的基础,文章零零散散有不少,但遗憾的是没有看到比较全面的系列文章.犹记得去年这个时候,参加 ...

  5. AWS re:Invent 2014回顾

    亚马逊在2014年11月11-14日的拉斯维加斯举行了一年一度的re:Invent大会.在今年的大会上,亚马逊一股脑发布和更新了很多服务.现在就由我来带领大家了解一下这些新服务. 安全及规范相关 AW ...

  6. Springlake-01 介绍&功能&安装

    1. 简介与功能 1)Springlake 是一个企业内容平台SECP 2)是一个可配置的系统,80%内容可以配置 3)允许建立和配置垂直解决方案 4)敏捷和占用空间小,可伸缩 5)端到端的安全性与性 ...

  7. webservice5

    如何访问webservice . 三种方式我知道, 但是, 方式1 只说明了如何访问wsdl, 不知道如何调用,现在就是需要知道如何像下面url描述一样 , http get .post 方式调用ws ...

  8. Oracle数据库分页的三种方法

    -- 不能对ROWNUM使用>(大于1的数值).>=(大于或等于1的数值).=(大于或等于1的数值),否则无结果-- 所以直接用只能从1开始-- rownum >10 没有记录,因为 ...

  9. AFNetworking+Python+Flask+pyOpenSSL构建iOS HTTPS客户端&服务器端

    对于HTTPS我在网上找了一堆资料看了下, 各种协议和证书已经有点晕了 最后我现有的感觉是, 在HTTP服务器上放一个证书, 在原本的HTTP访问之前客户端先检查证书是否正确 如果客户端证书检查正确, ...

  10. php-fpm进程数优化

    php-fpm未优化网友反映的问题 1.最近将Wordpress迁移至阿里云.由于自己的服务器是云服务器,硬盘和内存都比较小,所以内存经常不够使,通过ps ax命令查看后,发现启动php-fpm进程数 ...