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

有聚集索引的表叫聚集表。聚集索引保存了使用B树结构的聚集键,并只能以此顺序存储实际的数据。这也是SQL Server限制一个表只能有一个聚集索引,因为物理存储顺序只能有一个。我们来看看B树结构的逻辑呈现。下图是基于AdventureWorks2008R2数据库,表SalesOrderDetail创建的。

 USE IndexDB
GO
SELECT * INTO dbo.SalesOrderDetail FROM AdventureWorks2008R2.Sales.SalesOrderDetail
GO
CREATE UNIQUE CLUSTERED INDEX ix_SalesOrderDetail ON dbo.SalesOrderDetail(SalesOrderDetailID)

创建一个帮助表,并通过DBCC IND将表信息导入方便进一步分析。

 -- Create a helper table
CREATE TABLE sp_table_pages
(
PageFID TINYINT,
PagePID INT,
IAMFID TINYINT,
IAMPID INT,
ObjectID INT,
IndexID TINYINT,
PartitionNumber TINYINT,
PartitionID BIGINT,
iam_chain_type VARCHAR(30),
PageType TINYINT,
IndexLevel TINYINT,
NextPageFID TINYINT,
NextPagePID INT,
PrevPageFID INT,
PrevPagePID int,
PRIMARY KEY (PageFID, PagePID)
)
GO -- Write everything in a table for further analysis
INSERT INTO sp_table_pages EXEC('DBCC IND(IndexDB,SalesOrderDetail,-1)')
GO

提取跟节点/索引页,中间级/索引页,叶子节点/数据页信息。

 SELECT * FROM dbo.sp_table_pages WHERE IndexLevel=2 --根节点/索引页
DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,90,3) SELECT * FROM dbo.sp_table_pages WHERE IndexLevel=1 --中间级/索引页
DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,1864,3)
DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,1832,3)
DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,1808,3)
DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,1896,3) SELECT * FROM dbo.sp_table_pages WHERE IndexLevel=0 --叶子节点/数据页
DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,1704,3)
DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,1720,3)
DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,1752,3)
DBCC TRACEON(3604)
DBCC PAGE(IndexDB,1,1784,3)

根据上述信息进行聚集索引结构示例图绘制。

这张表有121317条记录。SQL Server需要3层来存储这个数据。我们根据上图来分析下页。在最高层,你可以看到只有一个页,这个叫做根页(root page)。在所有的B树结构里,都只有一个根页作为树结构的访问入口点。根层始终是最高层。在我们实例里根页有第2层索引。在根层(root level)和中间级别(intermediate level)的页叫索引页。在索引页里,SQL Server保存着聚集键(clustering key)和B树下层的入口点(页面指针)。聚集键保存的子页id,最小值保存在下层页(子页)。在指定子页上的聚集键最大值可以通过下一记录找到。例如,在根层第一条记录(Salesorderdetailid =NULL,pageid=1864),Salesorderdetailid小于等于30226可以在1864号页找到。入口(Salesorderdetailid =30226,pageid=1832))表示,salesorderdetailid值在30226与60003之间的记录可以在1832号页找到,以此类推。在那层,上一页和下一页的值将做双向链接来连接这些页。在根层因为只有一个页,所以上一页和下一页的值为0。

我们移到下一层来看,在这层有4个页。你可以在下一页和上一页里找到值,也是用来链接那层的页。这层被称为中间层(intermediate level)。中间层的个数和中间层的页数取决于表的大小和聚集键。一个大表可以有多个中间层,小表可能就没有中间层。这层的值可以和根层一样的方式读取。例如,这层的第1页的第一个入口(Salesorderdetailid =NULL,pageid=1704)表示,salesorderdetailid值小于等于72的可以在1704号页找到。

下一层是底层,称为叶子层(leaf level,index level 0)。在这层的页被称为叶子页(leaf pages)或数据页(data pages)。在这些页里,你可以找到Salesorderdetail表记录的全部数据(所有列)。换句话说,聚集索引的叶子层是实际数据存放的地方。

我们复制一张没有聚集索引的Salesorderdetail表。

 SELECT * INTO dbo.SalesOrderDetailHeap FROM AdventureWorks2008R2.Sales.SalesOrderDetail
GO

我们来执行下列查询,2个查询都返回同样的结果,这里我们更关注的是IO部分。

 SET STATISTICS IO ON
GO
SELECT * FROM SalesOrderDetail WHERE SalesOrderDetailID =75
GO
SELECT * FROM SalesOrderDetailheap WHERE SalesOrderDetailID =75

IO统计信息如下显示。有聚集索引的表,相比另一个表,逻辑读对它来说可以忽略不计的。

我们来看看SQL如何使用聚集索引的3个逻辑读取来拿到记录的。首先我们需要找出聚集索引的根节点(root node),DBCC IND命令可以帮我们找到。

 DBCC IND('IndexDB','SalesOrderDetail',1)

返回1501条记录,包含IAM页,一个索引页,和1499个数据页,部分结果显示如下。

从输出结果,我们可以知道90页(page type 2)是根页,这个页是这个表的入口点(entry point)。我们用DBCC PAGE看下这个页。

 DBCC traceon(3604)
GO
DBCC page('IndexDB',1,90,3)

SQL Server在子页保存聚集键的最小值,它的页号索引页里。例如,1864号页会有表salesorderdetailid列值小于等于30226的所有记录。同样,1832号页会有表salesorderdetailid列值在30226与60003之间的所有记录(30226可能在这2个页都有)。我们查找这条记录的salesorderdetailid值小于30226,所以这条记录的所有信息可以在子页1864找到。

我们用DBCC PAGE看下这个1864页。

 DBCC traceon(3604)
GO
DBCC page('IndexDB',1,1864,3)

输出结果包含410条记录,下面是部分结果显示。你可以参数1(最后一个参数)来运行DBCC PAGE命令来看页头。那样我们可以找到m_type=2的索引页。用我们刚才描述的方法,我们知道要找的记录(salesorderdetailid=75)可以在子页1705里找到。

我们看下1704页的内容:

 DBCC traceon(3604)
GO
DBCC page('IndexDB',1,1705,3) with tableresults

从页头部分,我们可以看到m_type是1,因此这个页是数据页,且是索引的叶子层。我们把如下输出结果一直往下翻。我们就有记录所有列的值,就是聚集索引叶子层,即实际的数据。

SQL Server从聚集索引只读取3页(根页,中间层的1页,还有叶子节点的1页,即数据页)就找到了我们的记录。

我们用DBCC IND命令比较下2个表的区别(SalesOrderDetail,SalesOrderDetailHeap)

 DBCC IND('IndexDB','SalesOrderDetail',1)
DBCC IND('IndexDB','SalesOrderDetailHeap',1)

SalesOrderDetailHeap表是堆表,只有1496个页;SalesOrderDetail表是聚集表,包含一个聚集索引,却有1501个页。这个多出的5页用来存储B树结构的中间级(intermediate)和根级(root)的索引页。我们当他是聚集索引的优点吧,多用5个页,却将逻辑读减少的只有3次。这个存储开销还是很划算的。

参考文章:

http://www.sqlservercentral.com/blogs/practicalsqldba/2013/03/12/sql-server-index-part-3-explaining-the-clustered-table-structure/

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

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

    在“索引深入浅出:非聚集索引的B树结构在聚集表”里,我们讨论了在聚集表上的非聚集索引,这篇文章我们讨论下在堆表上的非聚集索引. 非聚集索引可以在聚集表或堆表上创建.当我们在聚集表上创建非聚集索引时,聚 ...

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

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

  3. SQL Server - 索引详细教程 (聚集索引,非聚集索引)

    转载自:https://www.cnblogs.com/hyd1213126/p/5828937.html 作者:爱不绝迹 (一)必读:深入浅出理解索引结构 实际上,您可以把索引理解为一种特殊的目录. ...

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

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

  5. SQL Server索引 (原理、存储)聚集索引、非聚集索引、堆

    http://www.cnblogs.com/kissdodog/archive/2013/06/12/3132380.html

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

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

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

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

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

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

  9. SQL Server索引进阶:第三级,聚集索引

    原文地址: Stairway to SQL Server Indexes: Level 3, Clustered Indexes 本文是SQL Server索引进阶系列(Stairway to SQL ...

随机推荐

  1. \(\S1 \) Gaussian Measure and Hermite Polynomials

    Define on \(\mathbb{R}^d\) the normalized Gaussian measure\[ d \gamma(x)=\frac{1}{(2\pi)^{\frac{d}{2 ...

  2. [ASE]项目介绍及项目跟进——TANK BATTLE·INFINITE

    童年的记忆,大概是每周末和小伙伴们围坐在电视机前,在20来寸的电视机屏幕里守卫着这个至今都不知道是什么的白色大鸟. 当年被打爆的坦克数量估计也能绕地球个三两圈了吧. 十几年过去了,游戏从2D-3D,画 ...

  3. PAT/简单模拟习题集(一)

    B1001.害死人不偿命的(3n+1)猜想 (15) Description: 卡拉兹(Callatz)猜想: 对任何一个自然数n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把(3n+1)砍掉 ...

  4. 位运算(&)实现分享弹窗上的图标动态显示/隐藏

    一  需求     要求自定义弹窗,上面动态显示多种分享平台,根据后台api接口传递过来的type控制显示哪些平台icon     1 定义平台变量,用2的几次方来定value     2 若要显示那 ...

  5. JS、LUA都可以开发移动应用

    蓝色互动,大众点评.韩都衣舍.汉庭.携程旅游.华夏基金.中国农业银行等好多知名APP开发商,正在使用, 领先的快速移动应用开发平台 http://www.deviceone.net?00001 应用开 ...

  6. Windows Azure Service Bus Notification Hub推送通知

    前言 随着Windows Azure 在中国的正式落地,相信越来越多的人会体验到Windows Azure带来的强大和便利.在上一篇文章中, 我们介绍了如何利用Windows Azure中的Servi ...

  7. 用avalon实现一个完整的todomvc(带router)

    照着todomvc官网的例子,做了一个avalon版的todos,功能全都有了,而且加了router模块,比司徒大大写的都完善(≧▽≦)/~ js文件整整100行,初次使用avalon,书写过程中绕了 ...

  8. 假如现在有一堆长度大于3小于9的电话号码,用座机呼叫,如果出现这样的号码【123和12345】那么12345将永远不会被拨出,因为拨到123的时候电话已经呼出了,试写一个函数输出所有不能被呼出的电话号码(java实现)

    解题: 假如现在有一堆长度大于3小于9的电话号码,用座机呼叫,如果出现这样的号码[123和12345]那么12345将永远不会被拨出,因为拨到123的时候电话已经呼出了,试写一个函数输出所有不能被呼出 ...

  9. sonne_game网站开发02spring+mybatis框架搭建

    从最开始搭框架谈起,而且,我不仅仅会讲how,还会努力讲why.因为对于web开发,由于有太多好的框架.组件.工具,使得how往往不是那么深刻,背后的why更值得专研.如果有初学者关注我这个系列,也一 ...

  10. 我的c程序

    想写一个不同机器通信获取状态的c程序.遇到无数困难.断断续续了3.4周了,得到的结果仍然无法面世. 我想还是把其中遇到的所有困难写下来吧! 下面是结果 #include <stdlib.h> ...