原文地址:

Stairway to SQL Server Indexes: Level 10,Index Internal Structure

本文是SQL Server索引进阶系列(Stairway to SQL Server Indexes)的一部分。

在之前的级别中,我们从逻辑的角度介绍索引,集中于它们能为我们做什么。现在,是时候从物理的角度,并且检查一下索引的内部结构,从理解索引的内部结构,引导我们理解索引在上层做的工作。通过索引的结构,它是如何维护的,你可以理解在进行插入,更新,删除的时候,最小化索引的创建,修改,移动。

因此,从现在开始,我们除了要关心索引带来的好处,还要关心索引的消耗。毕竟,最小化消耗可以带来最大化的好处,带来最大化的好处是本系列的宗旨。

叶子和非叶子层

索引的机构由叶子和非叶子层组成。尽管没有明显的说明,我们之前的级别主要集中于索引的叶子层。因此,聚集索引的叶子层就是表本身,每个叶子层的入口都是表中的一行。对于非聚集索引来说,在叶子层每行都有一个入口(过滤索引除外),每个入口由索引键列,可选的包含列,以及标签组成,标签的内存是聚集索引的键列,或者RID(Row ID)。

索引入口也叫做索引行,不管它是表的一行(聚集索引叶子入口),还是表中一行的引用(非聚集索引叶子层),还是指向更低级别(非叶子层)的一页。

非叶子层是构建在叶子层上的结构,使得SQL Server可以完成下面的工作:

  • 以索引键的顺序维护索引的入口。
  • 根据给定的索引键值,快速的找到叶子层。

在第一级中,我们用电话本来介绍索引的好处。在电话本中,名叫“Meyer,Helen”的人,因为电话本是按照last name排序的,因此我们知道这个人应该再中间位置,直接跳到电话本的中间位置开始查找。但是SQL Server没有这种知识。它不知道哪一页是中间页,除非它从索引的开始访问到结束。因此,SQL Server在索引中构建了一些额外的结构。

非叶子层

这些额外的结构叫做非叶子层,也叫节点层。是构建在叶子层上的,不论页的物理位置在哪里。目的是给SQL Server指出每个索引的单独的页入口点,从一页到另一页的较短路劲。

在索引中的每一页,不管他是哪一层,都包含索引行或者入口。在叶子层的页中,每个入口点都指向表中的一行,或者就是表中的一行。如果表有十亿行数据,索引的叶子层就会有十亿个入口。

紧挨着叶子层的上一层,是最低的非叶子层,他的每一个入口都指向一个叶子层的页。如果我们的十亿个入口,平均每页有100个入口,叶子层将包含1,000,000,000/100=10,000,000页。如果最低的非叶子层包含10,000,000个入口,每个都指向叶子层的页,将包含10,000,000/100=100,000页。

每个较高的非叶子层的页中的入口,都指向下一层的页。因此,下一个较高的非叶子层就会有100,000个入口,1000个页。在上一层,就会是1000个入口,10页,再往上就是10个入口,1页,这就是最上面了。

索引顶端的页叫做根页。索引中,在根页之下,在叶子层之上的层叫做中间层。层数从0开始(叶子层就是0)向上增加。因此,中间层至少也是1.

非叶子层的入口只包含索引键的列和指向下一层页的指针。索引的包含列只存在于叶子层的入口,非叶子层的入口中没有这类信息。

索引中的每一页,除去根页,都包含两个额外的指针。一个指向下一页,一个指向上一页。页的双向链的结果就是,使得SQL Server可以正向或者反向扫描任何一层的页面。

简单例子

通过上图,可以说明索引的树形结构。我们在Personnel.Employee表的LastName和FirstName列创建了索引。

CREATE NONCLUSTERED INDEX IX_Full_Name
ON Personnel.Employee
(
LastName,
FirstName,
)
GO

指向页的指针除了包含页的编号,还包含数据文件的编号。如果一个指针是5:4567,表示指向#5文件的第4567页。

为了清除和明白,上面的索引和实际的索引在下面几个方面有一些不同:

每页的入口数量,在实际的索引中要比上面图中的多很多,每一层的页也要比图中的多很多。尤其是叶子层,在实际中会比图中多很多。

在实际的索引中,页上的入口是无序的。页入口的偏移指针,提供入口的顺序访问。(关于偏移指针可以查看第四级,页和分区中的介绍。)

索引的深度

根页的位置和索引的其他信息存储在一张系统表中。当SQL Server需要访问给定索引键值的索引入口的时候,它就用自己的方式从根页开始,访问每一层的每一页,直到包含索引键入口的叶子层。在我们十亿行表的例子中,SQL Server访问到需要的叶子层入口只需要读取5页;在上图的例子中,只需要读取3页就可以了。在聚集索引中,叶子层的入口就是实际的数据行,在非聚集索引中,入口可能是聚集索引的键,也可能是RID(Row ID)。

层的数目,也叫做深度,AdventureWorks数据库中,没有深度超过3的索引。数据库中如果有很大的表,或者索引键的列很多,深度有可能会超过6或者更深。

sys.dm_db_index_physical_stats函数给我们提供了一些索引的信息,包括索引的类型,深度,和大小;是一个表值函数,可以执行查询。下面的例子就是查看SalesOrderDetail表的索引信息。

SELECT OBJECT_NAME(P.OBJECT_ID) AS 'Table'
, I.name AS 'Index'
, P.index_id AS 'IndexID'
, P.index_type_desc
, P.index_depth
, P.page_count
FROM sys.dm_db_index_physical_stats (DB_ID(),
OBJECT_ID('Sales.SalesOrderDetail'),
NULL, NULL, NULL) P
JOIN sys.indexes I ON I.OBJECT_ID = P.OBJECT_ID
AND I.index_id = P.index_id;

结果显示如下。

下面的代码会显示表的指定的索引的信息,SalesOrderDetail表的uniqueidentifier列的非聚集索引,结果中的一行就是索引的一层。

SELECT OBJECT_NAME(P.OBJECT_ID) AS 'Table'
, I.name AS 'Index'
, P.index_id AS 'IndexID'
, P.index_type_desc
, P.index_level
, P.page_count
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('Sales.SalesOrderDetail'), 2, NULL, 'DETAILED') P
JOIN sys.indexes I ON I.OBJECT_ID = P.OBJECT_ID
AND I.index_id = P.index_id;

结果如下。

从上图的结果中,我们可以看出:

  • 索引的叶子层有408页。
  • 唯一的中间层只有2页。
  • 根层只有1页。

非叶子层的大小通常是叶子层的十分之一或者百分之二,这依赖于查询键包含哪些列,标签的大小,是否有包含列。换句话说,索引可能很大,也可能很小。

请记住,包含列只在非聚集索引中可用,他们只出现在叶子层的入口。在高层的入口中会忽略他们,这就是他们不增加非叶子层大小的原因。

因为聚集索引的叶子层就是表的数据行,在聚集索引中只有非叶子层额外的信息,需要额外的空间。无论是否创建索引,数据行都是存在的。因此,创建聚集索引的时候可能会花费一些时间,消耗一些资源,但是在创建完成之后,只需要很小的数据空间。

结论

索引的结构使得SQL Server可以快速的访问索引的入口。一旦发现入口,SQL Server就可以:

  • 访问入口的数据行。
  • 正向或者反向访问索引。

索引的树形结构已经使用了很长一段时间,甚至比关系数据库还要久远,随着时间它已经被证明很有用。

SQL Server索引进阶:第十级,索引内部结构的更多相关文章

  1. SQL索引管理器 - 用于SQL Server和Azure上的索引维护的免费GUI工具

    我作为SQL Server DBA工作了8年多,管理和优化服务器的性能.在我的空闲时间,我想为宇宙和我的同事做一些有用的事情.这就是我们最终为SQL Server和Azure 提供免费索引维护工具的方 ...

  2. SQL Server 解读【已分区索引的特殊指导原则】(3) - 非聚集索引分区

    一.前言 在MSDN上看到一篇关于SQL Server 表分区的文档:已分区索引的特殊指导原则,如果你对表分区没有实战经验的话是比较难理解文档里面描述的意思.这里我就里面的一些概念进行讲解,方便大家的 ...

  3. SQL Server 解读【已分区索引的特殊指导原则】(2)- 唯一索引分区

    一.前言 在MSDN上看到一篇关于SQL Server 表分区的文档:已分区索引的特殊指导原则,如果你对表分区没有实战经验的话是比较难理解文档里面描述的意思.这里我就里面的一些概念进行讲解,方便大家的 ...

  4. SQL Server数据库性能优化之索引篇【转】

    http://www.blogjava.net/allen-zhe/archive/2010/07/23/326966.html 性能优化之索引篇 近期项目需要, 做了一段时间的SQL Server性 ...

  5. SQL server 表中如何创建索引?

    SQL server 表中如何创建索引?看个示例,你就会了 use master goif db_id(N'zhangxu')is not nulldrop database zhangxugocre ...

  6. SQL Server查询性能优化——覆盖索引(二)

    在SQL Server 查询性能优化——覆盖索引(一)中讲了覆盖索引的一些理论. 本文将具体讲一下使用不同索引对查询性能的影响. 下面通过实例,来查看不同的索引结构,如聚集索引.非聚集索引.组合索引等 ...

  7. SQL Server查询性能优化——创建索引原则(一)

    索引是什么?索引是提高查询性能的一个重要工具,索引就是把查询语句所需要的少量数据添加到索引分页中,这样访问数据时只要访问少数索引的分页 就可以.但是索引对于提高查询性能也不是万能的,也不是建立越多的索 ...

  8. SQL Server 性能优化之——重复索引

    原文 http://www.cnblogs.com/BoyceYang/archive/2013/06/16/3139006.html 阅读导航 1. 概述 2. 什么是重复索引 3. 查找重复索引 ...

  9. SQL Server 查询性能优化——创建索引原则(一)(转载)

    索引是什么?索引是提高查询性能的一个重要工具,索引就是把查询语句所需要的少量数据添加到索引分页中,这样访问数据时只要访问少数索引的分页就可以.但是索引对于提高查询性能也不是万能的,也不是建立越多的索引 ...

  10. SQL Server 查询性能优化——创建索引原则(一)

    索引是什么?索引是提高查询性能的一个重要工具,索引就是把查询语句所需要的少量数据添加到索引分页中,这样访问数据时只要访问少数索引的分页就可以.但是索引对于提高查询性能也不是万能的,也不是建立越多的索引 ...

随机推荐

  1. Adobe Acrobat 9 Pro Extended 9.4简体中文完整免激活注册版

    Acrobat9 Pro最近升级比较频繁,如今已经升级到了Acrobat 9 Pro Extended 9.4版.亿品元素上曾经分享过Acrobat Pro Extended简体中文版 9.3.3 优 ...

  2. Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题

    这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...

  3. Android切换页面效果的实现一:WebView+ViewFlipper

    前言: 这两周在帮学校做一个新生入学用的“新里程”的项目,要做到页面切换阅读的效果,自己百度了下,找到普遍是使用WebView+ViewFlipper的实现方法,但这种方法不能满足我的要求,因为它很难 ...

  4. Saiku国际化总结

    国际化步骤: 1.在mondrian.properties同路径下加上locale_zh_CN.properties资源文件,内容例如:schema.name.K12UserAnalysis=K12用 ...

  5. xcode - 触摸移动

    第一步 创建一个UIView类  命名MoveView #import "MoveView.h" @implementation MoveView /** 移动事件 */ -(vo ...

  6. 你应该知道CSS选择器技巧

    什么是:before和:after? 该如何使用他们? :before是css中的一种伪元素,可用于在某个元素之前插入某些内容. :after是css中的一种伪元素,可用于在某个元素之后插入某些内容. ...

  7. Android - 不管在应用的哪个activity按Home键整个App就结束了

    最开始,客户反映说在用app的时候,来个电话,接完再点app,不是原来的界面,而是重启了.数据都没了,所以就在activity重写onSaveInstanceState方法,将数据保存起来.后经测试发 ...

  8. access 语句错误

    一直说是语句错误,一直没有找出来是什么错误,原来access的语句需要在字段上套一个[],这是最正确的写法,关键是动软生成的是我们一贯用的,和标准还是有些差别的,害了我好久都不知道是哪里的问题

  9. linux 分割文件

    import os import sysimport subprocess if len(sys.argv)<3 : print 'usage: filenum filename' file_n ...

  10. SQLserver数据库操作帮助类SqlHelper

    1 SqlHelper源码 using System; using System.Data; using System.Xml; using System.Data.SqlClient; using ...