SqlServer索引的原理与应用
索引的概念索引的用途:我们对数据查询及处理速度已成为衡量应用系统成败的标准,而采用索引来加快数据处理速度通常是最普遍采用的优化方法。 索引是什么:数据库中的索引类似于一本书的目录,在一本书中使用目录可以快速找到你想要的信息,而不需要读完全书。在数据库中,数据库程序使用索引可以重啊到表中的数据,而不必扫描整个表。书中的目录是一个字词以及各字词所在的页码列表,数据库中的索引是表中的值以及各值存储位置的列表。 索引的利弊:查询执行的大部分开销是I/O,使用索引提高性能的一个主要目标是避免全表扫描,因为全表扫描需要从磁盘上读取表的每一个数据页,如果有索引指向数据值,则查询只需要读少数次的磁盘就行啦。所以合理的使用索引能加速数据的查询。但是索引并不总是提高系统的性能,带索引的表需要在数据库中占用更多的存储空间,同样用来增删数据的命令运行时间以及维护索引所需的处理时间会更长。所以我们要合理使用索引,及时更新去除次优索引。 数据表的基本结构一个新表被创建之时,系统将在磁盘中分配一段以8K为单位的连续空间,当字段的值从内存写入磁盘时,就在这一既定空间随机保存,当一个 8K用完的时候,数据库指针会自动分配一个8K的空间。这里,每个8K空间被称为一个数据页(Page),又名页面或数据页面,并分配从0-7的页号,每个文件的第0页记录引导信息,叫文件头(File header);每8个数据页(64K)的组合形成扩展区(Extent),称为扩展。全部数据页的组合形成堆(Heap)。 SQLS规定行不能跨越数据页,所以,每行记录的最大数据量只能为8K。这就是char和varchar这两种字符串类型容量要限制在8K以内的 原因,存储超过8K的数据应使用text类型,实际上,text类型的字段值不能直接录入和保存,它只是存储一个指针,指向由若干8K的文本数据页所组成 的扩展区,真正的数据正是放在这些数据页中。? 页面有空间页面和数据页面之分。?? 当一个扩展区的8个数据页中既包含了空间页面又包括了数据或索引页面时,称为混合扩展(Mixed Extent),每张表都以混合扩展开始;反之,称为一致扩展(Uniform Extent),专门保存数据及索引信息。? 表被创建之时,SQLS在混合扩展中为其分配至少一个数据页面,随着数据量的增长,SQLS可即时在混合扩展中分配出7个页面,当数据超过8个页面时,则从一致扩展中分配数据页面。?? 空间页面专门负责数据空间的分配和管理,包括:PFS页面(Page free space):记录一个页面是否已分配、位于混合扩展还是一致扩展以及页面上还有多少可用空间等信息;GAM页面(Global allocation map)和SGAM页面(Secodary global allocation map):用来记录空闲的扩展或含有空闲页面的混合扩展的位置。SQLS综合利用这三种类型的页面文件在必要时为数据表创建新空间;?? 数据页或索引页则专门保存数据及索引信息,SQLS使用4种类型的数据页面来管理表或索引:它们是IAM页、数据页、文本/图像页和索引页。? 在WINDOWS中,我们对文件执行的每一步操作,在磁盘上的物理位置只有系统(system)才知道;SQL SERVER沿袭了这种工作方式,在插入数据的过程中,不但每个字段值在数据页面中的保存位置是随机的,而且每个数据页面在“堆”中的排列位置也只有系统 (system)才知道。?? 这是为什么呢?众所周知,OS之所以能管理DISK,是因为在系统启动时首先加载了文件分配表:FAT(File Allocation Table),正是由它管理文件系统并记录对文件的一切操作,系统才得以正常运行;同理,作为管理系统级的SQL SERVER,也有这样一张类似FAT的表存在,它就是索引分布映像页:IAM(Index Allocation Map)。?? IAM的存在,使SQLS对数据表的物理管理有了可能。?? IAM页从混合扩展中分配,记录了8个初始页面的位置和该扩展区的位置,每个IAM页面能管理512,000个数据页面,如果数据量太 大,SQLS也可以增加更多的IAM页,可以位于文件的任何位置。第一个IAM页被称为FirstIAM,其中记录了以后的IAM页的位置。?? 数据页和文本/图像页互反,前者保存非文本/图像类型的数据,因为它们都不超过8K的容量,后者则只保存超过8K容量的文本或图像类型数据。而索 引页顾名思义,保存的是与索引结构相关的数据信息。了解页面的问题有助我们下一步准确理解SQLS维护索引的方式,如页拆分、填充因子等。 ?页分裂一半的数据将保留在老页面,而另一半将放入新页面,并且新页面可能被分配到任何可用的页。所以,频繁页分裂,后果很严重,将使物理表产生大量数据碎片,导致直接造成I/O效率的急剧下降,最后,停止SQLS的运行并重建索引将是我们的唯一选择! 填充因子索引的一个特性,定义该索引每页上的可用空间量。FILLFACTOR(填充因子)适应以后表数据的扩展并减小了页拆分的可能性。填充因子是从0到100的百分比数值,设为100时表示将数据页填满。只有当不会对数据进行更改时(例如 只读表中)才用此设置。值越小则数据页上的空闲空间越大,这样可以减少在索引增长过程中进行页分裂的需要,但这一操作需要占用更多的硬盘空间。填充因子指定不当,会降低数据库的读取性能,其降低量与填充因子设置值成反比。 索引的分类SQL SERVER中有多种索引类型。 按存储结构区分:“聚集索引(又称聚类索引,簇集索引)”,“分聚集索引(非聚类索引,非簇集索引)” 按数据唯一性区分:“唯一索引”,“非唯一索引” 按键列个数区分:“单列索引”,“多列索引”。 聚集索引聚集索引是一种对磁盘上实际数据重新组织以按指定的一列或多列值排序。像我们用到的汉语字典,就是一个聚集索引,比如要查“张”,我们自然而然就翻到字典的后面百十页。然后根据字母顺序跟查找出来。这里用到微软的平衡二叉树算法,即首先把书翻到大概二分之一的位置,如果要找的页码比该页的页码小,就把书向前翻到四分之一处,否则,就把书向后翻到四分之三的地方,依此类推,把书页续分成更小的部分,直至正确的页码。 由于聚集索引是给数据排序,不可能有多种排法,所以一个表只能建立一个聚集索引。科学统计建立这样的索引需要至少相当与该表120%的附加空间,用来存放该表的副本和索引中间页,但是他的性能几乎总是比其它索引要快。 由于在聚集索引下,数据在物理上是按序排列在数据页上的,重复值也排在一起,因而包含范围检查(bentween,<,><=,>=)或使用group by 或order by的查询时,一旦找到第一个键值的行,后面都将是连在一起,不必在进一步的搜索,避免啦大范围的扫描,可以大大提高查询速度。 非聚集索引sqlserver默认情况下建立的索引是非聚集索引,他不重新组织表中的数据,而是对每一行存储索引列值并用一个指针指向数据所在的页面。他像汉语字典中的根据‘偏旁部首’查找要找的字,即便对数据不排序,然而他拥有的目录更像是目录,对查取数据的效率也是具有的提升空间,而不需要全表扫描。 一个表可以拥有多个非聚集索引,每个非聚集索引根据索引列的不同提供不同的排序顺序。 创建索引语法 CREATE [UNIQUE] CLUSTERED| NONCLUSTERED ] INDEX index_name ON { table | view } ( column ASC | DESC ,...n ] ) with[PAD_INDEX][[,]FILLFACTOR=fillfactor] ]IGNORE_DUP_KEY] ]DROP_EXISTING] ]STATISTICS_NORECOMPUTE] ]SORT_IN_TEMPDB] ] ON filegroup ] CREATE INDEX命令创建索引各参数说明如下: UNIQUE:用于指定为表或视图创建唯一索引,即不允许存在索引值相同的两行。 CLUSTERED:用于指定创建的索引为聚集索引。 NONCLUSTERED:用于指定创建的索引为非聚集索引。 index_name:用于指定所创建的索引的名称。 table:用于指定创建索引的表的名称。 view:用于指定创建索引的视图的名称。 ASC|DESC:用于指定具体某个索引列的升序或降序排序方向。 Column:用于指定被索引的列。 PAD_INDEX:用于指定索引中间级中每个页(节点)上保持开放的空间。 FILLFACTOR = fillfactor:用于指定在创建索引时,每个索引页的数据占索引页大小的百分比,fillfactor的值为1到100。 IGNORE_DUP_KEY:用于控制当往包含于一个唯一聚集索引中的列中插入重复数据时SQL Server所作的反应。 DROP_EXISTING:用于指定应删除并重新创建已命名的先前存在的聚集索引或者非聚集索引。 STATISTICS_NORECOMPUTE:用于指定过期的索引统计不会自动重新计算。 SORT_IN_TEMPDB:用于指定创建索引时的中间排序结果将存储在 tempdb 数据库中。 ON filegroup:用于指定存放索引的文件组。 例子: --表bigdata创建一个名为idx_mobiel的非聚集索引,索引字段为mobiel create index idx_mobiel on bigdata(mobiel) 表bigdata创建一个名为idx_id的唯一聚集索引,索引字段为id --要求成批插入数据时忽略重复值,不重新计算统计信息,填充因子为40 unique clustered index idx_id on bigdata(id) with pad_index,fillfactor=40,ignore_dup_key,statistics_norecompute管理索引Exec sp_helpindex BigData 查看索引定义 Exec sp_rename 'BigData.idx_mobiel',idx_big_mobiel' 将索引名由'idx_mobiel' 改为'idx_big_mobiel' drop index BigData.idx_big_mobiel 删除bigdata表中的idx_big_mobiel索引 dbcc showcontig(bigdata,idx_mobiel) 检查bigdata表中索引idx_mobiel的碎片信息 dbcc indexdefrag(Test,bigdata,idx_mobiel) 整理test数据库中bigdata表的索引idx_mobiel上的碎片 update statistics bigdata 更新bigdata表中的全部索引的统计信息 索引的设计原则对于一张表来说索引的有无和建立什么样的索引,要取决与where字句和Join表达式中。 一般来说建立索引的原则包括以下内容:
上文来自:http://www.cnblogs.com/knowledgesea/p/3672099.html ?在SQL Server中,索引是一种增强式的存在,这意味着,即使没有索引,SQL Server仍然可以实现应有的功能。但索引可以在大多数情况下大大提升查询性能,在OLAP中尤其明显.要完全理解索引的概念,需要了解大量原理性的知识,包括B树,堆,数据库页,区,填充因子,碎片,文件组等等一系列相关知识,这些知识写一本小书也不为过。所以本文并不会深入讨论这些主题。 ? 索引是什么??? 索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。 ??? 精简来说,索引是一种结构.在SQL Server中,索引和表(这里指的是加了聚集索引的表)的存储结构是一样的,都是B树,B树是一种用于查找的平衡多叉树.理解B树的概念如下图: ???? ??? 理解为什么使用B树作为索引和表(有聚集索引)的结构,首先需要理解SQL Server存储数据的原理. ??? 在SQL SERVER中,存储的单位最小是页(PAGE),页是不可再分的。就像细胞是生物学中不可再分的,或是原子是化学中不可再分的最小单位一样.这意味着,SQL SERVER对于页的读取,要么整个读取,要么完全不读取,没有折中. ??? 在数据库检索来说,对于磁盘IO扫描是最消耗时间的.因为磁盘扫描涉及很多物理特性,这些是相当消耗时间的。所以B树设计的初衷是为了减少对于磁盘的扫描次数。如果一个表或索引没有使用B树(对于没有聚集索引的表是使用堆heap存储),那么查找一个数据,需要在整个表包含的数据库页中全盘扫描。这无疑会大大加重IO负担.而在SQL SERVER中使用B树进行存储,则仅仅需要将B树的根节点存入内存,经过几次查找后就可以找到存放所需数据的被叶子节点包含的页!进而避免的全盘扫描从而提高了性能. ??? 下面,通过一个例子来证明: ???? 在SQL SERVER中,表上如果没有建立聚集索引,则是按照堆(HEAP)存放的,假设我有这样一张表: ????? ???? 现在这张表上没有任何索引,也就是以堆存放,我通过在其上加上聚集索引(以B树存放)来展现对IO的减少: ?????
理解聚集和聚集索引 ??? 在SQL SERVER中,最主要的两类索引是聚集索引和非聚集索引。可以看到,这两个分类是围绕聚集这个关键字进行的.那么首先要理解什么是聚集. ??? 聚集在索引中的定义: ????为了提高某个属性(或属性组)的查询速度,把这个或这些属性(称为聚集码)上具有相同值的元组集中存放在连续的物理块称为聚集。 ??? 简单来说,聚集索引就是: ???? ??? 在SQL SERVER中,聚集的作用就是将某一列(或是多列)的物理顺序改变为和逻辑顺序相一致,比如,我从adventureworks数据库的employee中抽取5条数据: ???? ??? 当我在ContactID上建立聚集索引时,再次查询: ???? ??? 在SQL SERVER中,聚集索引的存储是以B树存储,B树的叶子直接存储聚集索引的数据: ???? ??? 因为聚集索引改变的是其所在表的物理存储顺序,所以每个表只能有一个聚集索引. 非聚集索引 ???? 因为每个表只能有一个聚集索引,如果我们对一个表的查询不仅仅限于在聚集索引上的字段。我们又对聚集索引列之外还有索引的要求,那么就需要非聚集索引了. ???? 非聚集索引,本质上来说也是聚集索引的一种.非聚集索引并不改变其所在表的物理结构,而是额外生成一个聚集索引的B树结构,但叶子节点是对于其所在表的引用,这个引用分为两种,如果其所在表上没有聚集索引,则引用行号。如果其所在表上已经有了聚集索引,则引用聚集索引的页. ???? 一个简单的非聚集索引概念如下: ????? ???? 可以看到,非聚集索引需要额外的空间进行存储,按照被索引列进行聚集索引,并在B树的叶子节点包含指向非聚集索引所在表的指针. ???? MSDN中,对于非聚集索引描述图是: ????? ???? 可以看到,非聚集索引也是一个B树结构,与聚集索引不同的是,B树的叶子节点存的是指向堆或聚集索引的指针. ???? 通过非聚集索引的原理可以看出,如果其所在表的物理结构改变后,比如加上或是删除聚集索引,那么所有非聚集索引都需要被重建,这个对于性能的损耗是相当大的。所以最好要先建立聚集索引,再建立对应的非聚集索引. 聚集索引 VS 非聚集索引 ????? 前面通过对于聚集索引和非聚集索引的原理解释.我们不难发现,大多数情况下,聚集索引的速度比非聚集索引要略快一些.因为聚集索引的B树叶子节点直接存储数据,而非聚集索引还需要额外通过叶子节点的指针找到数据. ????? 还有,对于大量连续数据查找,非聚集索引十分乏力,因为非聚集索引需要在非聚集索引的B树中找到每一行的指针,再去其所在表上找数据,性能因此会大打折扣.有时甚至不如不加非聚集索引. ????? 因此,大多数情况下聚集索引都要快于非聚集索引。但聚集索引只能有一个,因此选对聚集索引所施加的列对于查询性能提升至关紧要. 索引的使用 ???? 索引的使用并不需要显式使用,建立索引后查询分析器会自动找出最短路径使用索引. ???? 但是有这种情况.当随着数据量的增长,产生了索引碎片后,很多存储的数据进行了不适当的跨页,会造成碎片(关于跨页和碎片以及填充因子的介绍,我会在后续文章中说到)我们需要重新建立索引以加快性能: ???? 比如前面的test_tb2上建立的一个聚集索引和非聚集索引,可以通过DMV语句查询其索引的情况: SELECT index_type_desc,alloc_unit_type_desc,avg_fragmentation_in_percent,fragment_count,avg_fragment_size_in_pages,page_count,record_count,avg_page_space_used_in_percent FROM sys.dm_db_index_physical_stats(DB_ID('AdventureWorks'),OBJECT_ID('test_tb2'),NULL,'Sampled') ? ????? ??? 我们可以通过重建索引来提高速度: ALTER INDEX idx_text_tb2_EmployeeID ON test_tb2 REBUILD ??? 还有一种情况是,当随着表数据量的增大,有时候需要更新表上的统计信息,让查询分析器根据这些信息选择路径,使用: UPDATE STATISTICS 表名 ?? 那么什么时候知道需要更新这些统计信息呢,就是当执行计划中估计行数和实际表的行数有出入时: ??? ?? 使用索引的代价??? 我最喜欢的一句话是”everything has price”。我们通过索引获得的任何性能提升并不是不需要付出代价。这个代价来自几方面. ??? 1.通过聚集索引的原理我们知道,当表建立索引后,就以B树来存储数据.所以当对其进行更新插入删除时,就需要页在物理上的移动以调整B树.因此当更新插入删除数据时,会带来性能的下降。而对于聚集索引,当更新表后,非聚集索引也需要进行更新,相当于多更新了N(N=非聚集索引数量)个表。因此也下降了性能. ??? 2.通过上面对非聚集索引原理的介绍,可以看到,非聚集索引需要额外的磁盘空间。 ??? 3.前文提过,不恰当的非聚集索引反而会降低性能. ????所以使用索引需要根据实际情况进行权衡.通常我都会将非聚集索引全部放到另外一个独立硬盘上,这样可以分散IO,从而使查询并行.
总结???? 本文从索引的原理和概念对SQL SERVER中索引进行介绍,索引是一个很强大的工具,也是一把双刃剑.对于恰当使用索引需要对索引的原理以及数据库存储的相关原理进行系统的学习. 上文来自:http://www.cnblogs.com/CareySon/archive/2011/12/22/2297568.html 一、存储结构在SQL Server中,有许多不同的可用排列规则选项。 二进制:按字符的数字表示形式排序(ASCII码中,用数字32表示空格,用68表示字母"D")。因为所有内容都表示为数字,所以处理起来速度最快,遗憾的是,它并不总是如人们所想象,在WHERE子句中进行比较时,使用该选项会造成严重的混乱。 字典顺序:这种排序方式与在字典中看到的排序方式一样,但是少有不同,可以设置大量不同的额外选项来决定是否区分大小写、音调和字符集。 1、平衡树(B-树) 平衡树或B-树仅是提供了一种以一致且相对低成本的方式查找特定信息的方法。其名称中的"平衡"是自说明的。平衡树是自平衡的,这意味着每次树进行分支时都有接近一半的数据在一边,而另一半数据在另一边。树命名的由来是因为,如果绘制该结构,再倒过来,发现很像一棵树,因此称树。 平衡树始于根节点。如果有少量的数据,这个根节点可以直接指向数据的实际位置。 结构图:
因此,从根节点开始并浏览记录,直到找到以小于查找值的值开始的最后一页。然后获得指向该节点的一个指针并且浏览它。直到找到想要的行。 当数据很多时,根节点中指向中间的节点(非页级节点)。非页级节点是位于根节点和说明数据的物理存储位置的节点之间的节点 根节点->中间节点(非叶级节点)[n个]->存储位置节点(叶级节点) 非叶级节点可以指向其他非叶级节点或叶级节点。叶级节点是从中可获得实际物理数据的引用的节点。
? 从上图可以看出,查找开始于根节点,然后移动到以等于或小于查找值的最高值开始的同时也在下一级节点中的节点。然后重复这个过程-查找具有等于或者小于查找值的最高起始值节点。继续沿着树一级一级往下,直到二级节点-从而知道数据的物理位置。 2、页拆分 所有这些页在读取方面工作良好-但在插入时会有点麻烦。前面提到B-树结构,每次遇到树中的分支时,因为每一边都大约有一半的数据,所以B-树是平衡的。另外,由于添加新数据到树的方法一般可避免出现不平衡,所以B-树有时被认为是自平衡的。 通过将数据添加到树上,节点最终将变满,并且将需要拆分。因为在 SQL Server中,一个节点相当于一个页-所以这被称为页拆分。如图所示: ? 当发生页拆分时,数据自动地四处移动以保持平衡,数据的前半部分保留在旧页上,而数据的剩余部分添加到新页 - 这样就形成了对半拆分,使得树保持平衡。 如果考虑下这个拆分过程,将认识到它在拆分时增加了大量的系统开销。不只是插入一页还将进行下列操作:
注意最后一条,如果在父节点中添加记录时,父页也满了引起拆分,整个过程会重新开始。甚至会影响到根节点。并且,如果根节点拆分,那么实际最终会创建两个额外的页,因此只能有一个根节点,所以之前的根节点的页被拆分成两个页,而且称为树的新中间级别节点。然后创建全新的根节点,并且将有两个记录项,指向刚刚由根节点分拆出来的两个中间节点。 由上面的原理可以知道,当向树的上层移动时,页拆分的数量变得越来越少。因为下级的一个页拆分对上级来说是一条记录。 虽然SQL Server有许多不同类型的索引,但是所有这些索引都以某种方式利用这种平衡树方法。事实上由于平衡树的灵活特性,所有索引在结构上都非常类似,不过他们实际上还有一点点区别,并且这些区别会对系统的性能产生影响。 3、SQL Server中访问数据的方式 从广义上讲,SQL Server检索所需数据的方法只有两种:
1、使用全表扫描 表扫描是相当直观。当执行表扫描时,SQL Server从表的物理起点处开始,浏览表中的每一行。当发现和查询条件匹配的行时,就在结果集中包含它们。关于表扫描很多说法都是效率低,但是如果表数据减少的情况下,实际上使用表扫描却是最快的。 2、使用索引 在查询优化过程中,优化器查看所有可用的索引结构并且选择最好的一个(这主要基于在连接和WHERE子句中所指定的信息,以及SQL Server在索引结构中保存的统计信息)。一旦选择了索引,SQL Server将在树结构中导航至与条件匹配的数据位置,并且只提取它所需的记录。与表扫描的区别在于,因为数据时排序的,所以查询引擎知道它何时到达正在查找的当前范围的下界。然后它可以结束查询,或者根据需要移至下一数据范围。EXISTS的工作方式是查到匹配的记录SQL Server就立即停止。使用索引所获得的性能与使用EXISTS类似甚至更好,因为查找数据的过程的工作方式是类似的;也就是说,服务器可能使用某种索引知道何时没有相关内容,并且立即停止。此外,可以对数据执行非常快速的查找(称为SEEK),而不是在整个表中查找。 3、索引类型和索引导航 尽管表面上在SQL Server中有两种索引结构(聚集索引和非聚集索引),但就内部而言,有3种不同的索引类型。
物理数据的存储方式在聚集索引和非聚集索引中是不同的。而SQL Server遍历平衡树以到达末端数据的方式在所有3种索引类型中也是不同的。 所有的SQL Server索引都有叶级和非叶级页,叶级是保存标识记录的“键”的级别,非叶级是叶级的引导者。 索引在聚集表(如果表有聚集索引)或者堆(用于没有聚集索引的表)上创建。 (1)、聚集表 聚集表是在其上具有聚集索引的任意表。但是它们对于表而言意味着以指定顺序物理存储数据。通过使用聚集索引键唯一地标志独立的行-聚集键即定义聚集索引的列。 如果聚集索引不是唯一的,那将怎样?如果索引不是唯一索引,那么聚集索引如何用于唯一地标志一行?SQL Server会在内部添加一个后缀到键上,以保证行具有唯一的标识符。 (2)、堆 堆是在其上没有聚集索引的一个表。在这种情况下,基于行的区段、页以及行偏移量(偏移页顶部的位置)的组合创建唯一的标识符,或者称为行ID(RID)。如果没有可用的聚集键(没有聚集索引),那么RID是唯一必要的内容。堆表并不是B树结构。 4、聚集索引 聚集索引对于任意给定的表而言是唯一的,一个表只能有一个聚集索引。不一定非要有聚集索引。聚集索引特殊的方面是:聚集索引的叶级是实际的数据-也就是说,数据重新排序,按照和聚集索引排序条件声明的相同物理顺序存储。这意味着,一旦到达索引的叶级,就到达了数据。而非聚集索引,到达了叶级只是找到了数据的引用。 任何新记录都根据聚集列正确的物理顺序插入到聚集索引中 。创建新页的方式随需要插入记录的位置而变化。如果新记录需要插入到索引结构中间,就会发生正常的页拆分。来自旧页的后一半记录被移到新页,并且在适当的时候,将新记录插入到新页或旧页。如果新记录在逻辑上位于索引结构的末端,那么创建新页,但是只将新记录添加到新页。
从数据插入的角度看,这里应该能看到用int类型作为聚集索引的好处。 为了说明索引是表的顺序,请看一下表:
然后在Id列建立聚集索引: CREATE CLUSTERED INDEX Index_Name ON Person(Id) --建立Id列聚集索引 执行查询语句: select top 3 * from Person
再执行查询语句: 输出结果如下:
留意到同样的语句,返回已经改变。可以聚集索引是表的顺序,会影响到top语句。 5、导航树 在SQL Server中甚至索引也是存储在平衡树中,在理论上,平衡树在作为树分支的每个可能方向上总是具有一般的剩余信息。聚集索引的平衡树形式如下图所示。
? 在这里,执行对数字158-400的范围查询(聚集索引非常擅长的事情),只需要导航到第一个记录,并且包含在该页上的所有剩余记录。之所以知道需要该页的剩余部分,是因为来自于上一级节点的信息也需要来自一些其他页的数据。因为这是有序表,所以可以确信它是连续的-这意味着如果下一页有符合条件的记录,那么这个页的剩余部分必须被包含。无需任何验证。 首先导航到根节点。SQL Server能够给予Sys.indexes系统元数据视图中保存记录项定位根节点。 光说不练,纯属诈骗,下面以一个1万行的PersonTenThousand表来说明B树结构对数据页读取的提升。 表的内容大致如下:
一开始这张表并没有任何索引:
由于此表上没有索引,因此只能够通过堆表扫描获得所需数据,因此,无论是检索Id,还是Name列,都要整张表扫描一次。因此预读,逻辑读都要读取所有的数据页。 下面在该表的Id列建立一个聚集索引: CLUSTERED INDEX index1 ON PersonTenThousand(ID)再来执行相同的查询:
我们看到,由于ID列是聚集索引,因此根据ID查找,B树结构的优点就充分发挥了出来,只需要2次物理读就能够定位到数据。 而Name列上没有索引,因此还是需要预读838次(还是聚集表扫描)才能定位到数据。 以上例子充分说明了B-树结构的优点。 6、非聚集索引 6.1 非聚集索引优点: 1、因为在SQL Server中一页只是8K,页面空间有限,所以一行所包含的列数越少,它能保存的行就越多。非聚集索引通常不包含表中所有的列,它一般只包含非常少数的列。因此,一个页上将能包含比表行(所有的列)更多行的非聚集索引。因此,同样读取一页,在非聚集索引中可能包含200行,但是在表中可能只有10行,具体数据有表行的大小以及非聚集列的大小确定。 2、非聚集索引的另一个好处是,它有一个独立于数据表的结构,所以可以被放置在不同的文件组,使用不同的I/O路径,这意味着SQL Server可以并行访问索引和表,使查找更快速。 下面说明一下,非聚集索引的好处:
假设有一个单列的表,共有27行,每一页上存了3行。没有顺序,假如我们要从中查找值为5的行,那么需要的读次数为9,因为它必须扫描到最后一页,才能够确定所有页都不存在值为5的行了。 假如建立了非聚集索引:
再次查找值为5的行,那么需要的读次数为2,为什么?因为非聚集索引是有顺序的,当SQL Server读取到值为6的那一行时,就知道不必再读下去了。那么如果要读取值为25的页呢?还是需要9个读操作。因为它刚巧就在最后一页。恰好这个东西,可以通过B树结构来优化。B树算法最小化了定位所需的键值访问的页面数量,从而加速了数据访问过程。 6.2 非聚集索引的开销 索引给性能带来的好处有一定的代价。有索引的表需要更多的存储和内存空间容纳数据页面之外的索引页面。数据的增删改可能会花费更长的时间,需要更多的处理时间以维护不断变化的表的索引。如果一个INSERT语句添加一行到表中,那么它也必须添加一行到索引结构中。如果索引是一个聚集索引,开销可能会更大,因为行必须以正确的顺序添加到数据页面(当然分int聚集列和string聚集列会不同)。UPDATE和DELETE类似。 虽然索引对增删改有一定的影响,但是别忘了,要UPDATE或DELETE一行的前提是必须找到一行,因此索引实际上对于有复杂WHERE条件的UPDATE或DELETE也是有帮助的。在使用索引定位一行的有效性通常能弥补更新索引所带来的额外开销。除非索引设计不合理。 7、堆上的非聚集索引 在这里要说明一点,无论是在堆上还是在聚集列上,非聚集索引都是排序后存储的。按非聚集索引列排序。 堆上的非聚集索引和聚集索引在大多数方面以类似的方式工具。其差别如下: 叶级不是数据-相反,它是一个可从中获得指向该数据的指针的级别。该指针以RID的形式出现(堆上一RID出现,聚集表上以聚集键出现),这种RID由索引指向的特定行的区段、页以及行偏移量构成。即叶级不是实际的数据,使用叶级也仅仅比使用聚集索引多一个步骤。因为RID具有行的位置的全部信息,所以可以直接到达数据。 差了一个步骤,实际上差别的系统开销是很大的。 使用聚集索引,数据在物理上是按照聚集索引的顺序排列的。这意味着,对于一定范围的数据,当找到在其上具有数据范围起点的行时,那么很可能有其他行在同一页上(也就是说,因为他们存储在一起,所以在物理上已几乎到达下一个记录)。 使用堆,数据并未通过除索引外的其他方法连接在一起。从物理上看,绝对没有任意种类的排序。这意味着从物理读取的角度看,系统不得不从整个文件中检索记录。实际上,很可能最终多次从同样的页中取出数据。SQL Server没有方法指导它将需要回到该物理位置,因为在数据之间没有连接。因此,堆上的非聚集索引的工作方式是:通过扫描堆上的非聚集索引,找到(Row_Number行号),每找到一个RID,再通过RID取得数据。如果搜索是返回多个记录,则性能可能比不上扫描全表。下图显示用堆上的非聚集索引执行与上面聚集索引相同的查询:
主要通过索引导航,一切都按以前的方式工作,以相同的根节点开始,,并且遍历数,处理越来越集中的页。直到到达索引的叶级。这里有了区别。以聚集索引的方式,能够正好在这里停止,而以非聚集索引的方式,则需要做更多的工作。如果索引是在堆上,那么只要在进入一个级别,获得来自叶级页的RID,并且定位到该RID-直到这时才可以直接获得实际的数据。 8、聚集表上的非聚集索引 使用聚集表上的非集群索引时,还有一些类似性-但同样也有区别。和堆上的非集群索引一样,索引的非叶级及诶单的工作与使用聚集索引时几乎一样。区别出现在叶级。 在叶级,与使用其他两种索引结构所看到的内容有相当明显的区别。聚集表上的非集群索引有另外一个索引来查找。使用聚集索引,当到达叶级时,可以找到实际的数据,当使用堆上的非集群索引,不能找到实际的数据,但是可以找到能够直接获得数据的标识符(仅仅多了一步)。使用聚集表上的非聚集索引,可以找到聚集键。也就是说,找到足够的信息继续并利用聚集索引。 以上理解,说白了就是,当使用非聚集索引时,就是遍历非聚集索引找到聚集索引,最后多次采用聚集索引找到数据。 最终结果如下图所示:
? 首先是一个范围搜索。在索引中执行一次单独的查找,并且可以浏览非聚集索引以找到满足条件(T%)的连续数据范围。这种能够直接到达索引中的特定位置的查找被称为seek。 ? 然后第二个查找-使用聚集索引查找,第二种查找非常迅速:问题在于它必须执行多次。可以看到。SQL Server从第一个索引中查找检索列表(所有名字以"T"开始的列表),但是该列表在逻辑上并没有以任意连续的方式与聚集键相匹配-每个记录单独地查找。图下图所示:
自然,这种多个查找的情况比一开始仅能使用聚集索引引入了更多的系统开销。第一个索引查找-通过非聚集索引的方法-只需要非常少的逻辑读操作。 注意上图,使用聚集表上的非聚集索引,找到的是一个聚集索引键的列表。然后用这个列表,逐个使用聚集索引查找到所需的数据。 ? 注意,如果表没有聚集索引,建立了非聚集索引,那么非聚集索引使用的是行号,如果此时你又添加了聚集索引,那么所有的非聚集索引引用的RID都要改为聚集索引键。这对性能的消耗是非常大的,因此最好先建立聚集索引,在建立非聚集索引。 关于索引的几个要点: ?
上文来自:http://www.cnblogs.com/kissdodog/archive/2013/06/12/3132380.html (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |