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

时间:2021-10-17 17:07:44

一个表只能有一个聚集索引,数据行以此聚集索引的顺序进行存储,一个表却能有多个非聚集索引。我们已经讨论了聚集索引的结构,这篇我们会看下非聚集索引结构。

非聚集索引的逻辑呈现

简单来说,非聚集索引是表的子集。当我们定义了一个非聚集索引时,SQL Server把整套非聚集索引键存在不同的页里。我们来看下一个包含BusinessEntityID(PK),PersonType,FirstName,LastName这4列的表,这个表上有一个非聚集索引定义。主体表按BusinessEntityID列(聚集索引键)的顺序存储。非聚集索引的存储是与主体表分离的。如果你仔细看非聚集索引表,你会发现,记录是按Firstname,lastname 列的顺序排列的。简单理解下,非聚集索引就是主体表的子集。

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

假设现在我们要找出first name值为Michael的记录。如果你从实体表找的话,我们需要从头到脚把每条记录匹配一次,因为记录并没有按first name列排序保存。如果这个表记录有上千条记录的话,这将是一个非常无聊且费时的工作。如果我们在非聚集索引表里找将会容易很多,因为这个表是按first name列以字母顺序排列的。我们很容易定位到first name是Michael的记录。我们并不需要再往下找,因为我们确定没有更多的first name是Michael的记录了。

现在我们得到了Firstname,lastname的值。那我们如何拿到其它2列的值?让我们对非聚集索引做一些改动,将BusinessEntityID列也作为非聚集索引。

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

现在,一旦我们定位到记录,我们可以使用BusinessEntityID(聚集索引键)列返回主体表,得到其他列的值,这个操作被称为书签查找(bookmark lookups)或RID查找。

聚集索引与非聚集索引

非聚集索引和聚集索引有一样的B树结构。非聚集索引键不会对主体表的数据排序做任何改变,因为聚集索引强制SQL Server将数据以聚集索引键的顺序存储。聚集索引的叶子层由包含表具体数据的数据页组成,而非聚集索引的叶子层由索引页组成。

非聚集索引可以定义在堆表聚集表。在非聚集索引的叶子层,每个索引行包含非聚集索引键值和行定位器。这个定位器指向聚集索引或堆表的数据行。在非聚集索引行里的行定位器要么指向行,要么指向行聚集索引键。如果是堆表,它没有聚集索引,行定位器是个指向行的指针。这个指针由页里行的(文件号:页号:槽号,file identifier :page number :slot number)组成。整个指针被称为ROW ID(RID)。如果表有聚集索引,行定位器是行的聚集索引键。

非聚集索引深入浅出

我们用文章“索引深入浅出:聚集索引的B树结构”用到的salesorderdetails创建一个非聚集索引,这个表在salesorderdetailid列有一个聚集索引。

 CREATE UNIQUE INDEX Ix_ProductId ON SalesOrderDetail(ProductId,Salesorderid) 

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

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

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

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

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

 DBCC IND(IndexDB,SalesOrderDetail,2)

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

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

我们看到输出结果一共有229条记录,包含1个IAM页和229个索引页。我们可以通过找IndexLevel 列值最大的记录,来找根页(root page)。记住索引层级是从叶子层向根层增长的。

 SELECT * FROM dbo.sp_table_pages ORDER BY IndexLevel DESC --根节点/索引页

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

在这个表里,我们根层(root leve)页号是3472,index level是1,这就是说,这个非聚集索引的B树结构只有根层(root level)和叶子层(leaf level),没有中间层(intermediate level)。我们来看看3472页。

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

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

返回结果一共有227条记录(227个叶子层的索引页)。部分结果如上所示。这和聚集索引里的根层(root)/中间层(intermediate)的页结构是一样的。productid与salesorderid组合的值小于或等于(707,51151)的所有记录,可以在子页3416里找到。productid与salesorderid组合的值在(707,51151)与(707,55920)之间的所有记录,可以在子页3417里找到,并以此类推。

我们来看看3417页。

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

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

一共返回539条记录,都是product id为707的记录。这里的索引只用2层,这个是B树结构的叶子层。你会注意到,这里没有子页ID列,但我们有salesorderdetailid列(聚集索引键),SQL Server用它来进行键或书签查找操作。

我们来看看,SQL Server如何使用这个索引进行一个SELECT操作。点击工具栏的索引深入浅出(4/10):非聚集索引的B树结构在聚集表显示包含实际的执行计划。

 SET STATISTICS IO ON
GO
SELECT * FROM SalesOrderDetail WHERE productid=707 AND SalesOrderid=51192

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

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

可以看到执行计划的键查找操作。因为这里where条件刚好完全符合我们非聚集索引定义,SQL Server用这个索引来执行查询。首先SQL Server读取B树结构的根页。我们的查询条件组合(707,51192)落在根页的第二条记录上,因此SQL Server走到它的子页(页号3417)。在这个页里,我们可以用条件组合(707,51192)定位到具体的记录上,它的salesorderdetailid值是37793。从这里开始,SQL Server使用salesorderdetailid值进行键查找(key look up)操作。从上一个文章知道,但我们进行任何聚集索引键查找是,需要执行3个I/O。 因此这里,SQL Server需要执行5个I/O操作(2个在非聚集索引,3个在聚集索引的书签/键查找(bookmark/key lookup),这个和你的结果输出一致。

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

为了更好的理解它,我们可以把非聚集索引当作salesorderdetail 表的一个子表(我们把它叫做Saleorderdetail_NC),有productid,salesorderid 和 SalesorderDetailid列,并且 ProductId与salesorderid列组合为聚集索引。上述查询的结果可以通过以下2个查询来获得。

 SELECT *  FROM SalesOrderDetail_nc WHERE productid=707 AND SalesOrderid=51192
GO
SELECT * FROM SalesOrderDetail WHERE SalesOrderDetailid=37793

我们再来看一个查询:

1 SELECT *  FROM SalesOrderDetail WHERE productid=707

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

查询返回3083条记录,查询条件与非聚集索引的第一列匹配。但是SQL Server并没用非聚集索引来执行这个查询,查询计划如下所示。

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

这样做的原因是,如果使用非聚集索引,就需要为3083条记录执行书签查找(key lookup)。这会产生9249个I/O操作(3083*3)。因此,SQL Server使用了聚集索引扫描,它只需要1501(对于聚集索引树结构需要的页数)个I/O操作。如果我们做一个小的改动,只要Productid ,SalesOrderDetailid和SalesOrderId列,SQL Server会使用非聚集索引,因为它不需要进行书签查找(bookmark lookup)操作。非聚集索引的叶子层已经包含这些列了。

 SELECT productid,salesorderdetailid,salesorderid  FROM SalesOrderDetail WHERE productid=707

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

这篇文章真的有点长,而且我是该死的BING输入法出错,导致浏览器崩溃,丢失一个晚上3个小时成果,重新写好的,希望大家看了之后可以透彻理解非聚集索引了,晚安各位!!2015-05-14 00:18:42

参考文章:

http://www.sqlservercentral.com/blogs/practicalsqldba/2013/03/14/sql-server-part-4-explaining-the-non-clustered-index-structure-/

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

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

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

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

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

  3. Oracle非分区索引,全局分区索引和本地分区索引。

    1.如果按照索引是否分区作为划分依据,Oracle 的索引类型可以分为非分区索引,全局分区索引和本地分区索引. 2.创建演示实例 --创建非分区表create table test_partition ...

  4. SQL Server性能优化(10)非聚集索引的存储结构

    一,新建测试表 CREATE TABLE [dbo].[Users]( ,) NOT NULL, ) NOT NULL, [CreatTime] [datetime] NOT NULL ) ON [P ...

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

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

  6. SQL Server的聚集索引和非聚集索引

    微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引.簇集索引)和非聚集索引(nonclustered index,也称非聚类索引.非簇集索引)…… (一) ...

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

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

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

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

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

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

随机推荐

  1. JavaScript时间处理之几个月前或几个月后的指定日期

    在平常项目开发过程中,经常会遇到需要在JavaScript中处理时间的情况,无非两种(1,逻辑处理  2,格式转换处理).当然要说相关技术博,园子里闭着眼睛都能抓一把,但是我要做的是:既然有幸被我碰到 ...

  2. c++11 实现单例模式

    C++11出来后,里面新增加了好多好用的功能 下面的单例就是使用了C++11中的标准库中的mutex和unique_prt 进行内存管理的. 此单例模式不用担心内存的释放问题 #pragma once ...

  3. Question2Answer安装

    Question2Answer安装 Question2Answer的安装过程很简单,只需要几分钟的时间你就可以有一个强大的问答系统. 安装要求 Web服务器(比如Apache) PHP 4.3 或更高 ...

  4. Android ListView SimpleAdapter支持Bitmap类型图片显示

    // 处理simpleAdapter中包括bitmap类型 adapter.setViewBinder(new ViewBinder() { public boolean setViewValue(V ...

  5. 【.NET】上传文件,生成缩略图

    类名:Upload using System; using System.Collections; using System.ComponentModel; using System.Data; us ...

  6. JavaBean+Servlet 开发时,JavaBean 编写问题

    在开发 JavaBean 时,遇见一个问题: *****  表单字段为空,提交时出现 nullPointerException 异常:  表单字段不为空,提交正常. 使用 JavaBean ,JSP页 ...

  7. 纯HTML和CSS实现点击切换

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. python第三十一课--递归&lpar;2&period;遍历某个路径下面的所有内容&rpar;

    需求:遍历某个路径下面的所有内容(文件和目录,多层级的) import os #自定义函数(递归函数):遍历目录层级(多级) def printDirs(path): dirs=os.listdir( ...

  9. 玩转Eclipse — 自动代码生成的Java Code Template

    文章转载地址:点击打开链接 当代码写到一定程度之后,就会发现很多代码都被重复地敲了N多遍,甚至毫不夸张地说:闭着眼睛都能敲出来.大量地敲这些重复地代码,除了锻炼敲键盘的速度,基本上没有其他益处,但是长 ...

  10. PWA 入门&colon; 写个非常简单的 PWA 页面

    Progressive Web Apps 是 Google 提出的用前沿的 Web 技术为网页提供 App 般使用体验的一系列方案. 这篇文章里我们来完成一个非常简单的 PWA 页面. 一个 PWA ...