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

标签: sql server 性能优化 | 发表时间:2012-09-19 20:56 | 作者:DotNet菜园
出处:http://www.cnblogs.com/

 

     索引是什么?索引是提高查询性能的一个重要工具,索引就是把查询语句所需要的少量数据添加到索引分页中,这样访问数据时只要访问少数索引的分页就可以。但是索引对于提高查询性能也不是万能的,也不是建立越多的索引就越好。索引建少了,用WHERE子句找数据效率低,不利于查找数据。索引建多了,不利于新增、修改和删除等操作,因为做这些操作时,SQL SERVER除了要更新数据表本身,还要连带地立即更新所有的相关索引,而且过多的索引也会浪费硬盘空间。因此要建得恰到好处,这就需要经验了。

 

一:索引的基本目的

     索引的基本目的是在大量数据中找寻少量数据。你可以想像一下,若一本书有700页,就像数据表有700个数据页,而索引却有600个索引页,你会想用索引来查询书籍的内容吗?

     索引字段的值重复性越低越好,假设书籍中如“的”“了”这些在文章中重复性极高的字,每页都有一大堆,你会先翻索引页某个位置有“的”,翻回该页读取了“的”之后,再索引看下一个“的”,结果是在先前同一页的不同位置,又翻回书籍原页查看下一个“的”。

     那么怎么理解索引是从大量数据中寻找少量数据呢?下面我们举个例子来说明。

     如果一个数据表的记录平均长度为400字节,则100万条记录需要5万个数据页,其计算公式如下:

  1000000/(8060/400)=50000

  如果该数据表建立聚集索引,键值为4个字节长度,而ID的数据长度为13个字节,因此索引结构每条记录为20个字节。

  4(聚集索引键值)+13(ID键值)+3(管理信息)=20

  以ID字段所建立的索引,100%填充率,则总分页数约为2482页,其计算方式如下:

  1000000/(8060/20)

  即使是使用80%的填充率来计算也只有3106页。其计算方式如下:

  1000000/((8060*0.8)/20)

  从上面可以看出如果是第一种情况,则索引页只占到总数据页的5%:

  2482/50000=0.04964 

  即使考虑取每页只填充80%的索引数据,第二种情况,索引页也只是占总数据页的6%:

  3106/50000=0.06212 

  再说如果查询条件中的字段建立索引,则由于索引键值数据都是以B-Tree有顺序的摆放,所以可采用二分查找找数据。也就是2的N次方大于记录数,就可以找到该条数据。而2的20次方大于100万,因此最多找寻20次就可以找到该条记录。由于比较次数少,数据结构也小,节省访问硬盘与内在的资源,索引将大幅提升找寻数据的效率。SQL SERVER为提高访问与查找对比的效率,用来作索引的数据域键值愈小愈好,也就是要让分页尽量存更多的键值记录。

 

 注:

  如果未使用 UNIQUE 属性创建聚集索引,数据库引擎将向表自动添加一个 4 字节的 uniqueifier 列。必要时,数据库引擎将向行自动添加一个 uniqueifier 值以使每个键唯一。此列和列值供内部使用,用户不能查看或访问。

 

 

二:什么是索引

  在 SQL Server 中,索引是按 B 树结构进行组织的。如下图。

            

 

  您也可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。下面,举例来说明一下聚集索引和非聚集索引的区别:

  其实,新华字典的正文本身就是一个聚集索引。比如,我们要查“按”字,就会很自然地翻开字典的前几页,因为“按”的拼音是“an”,而按照拼音排序的新华字典是以英文字母“a”开头并以“z”结尾的,那么“按”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明新华字典中没有这个字;同样的,如果查“招”字,那也会将新华字典翻到最后部分,因为“招”的拼音是“zhao”。也就是说,新华字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。

  如果您碰到一个不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63 页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后 再翻到您所需要的页码。我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。

 

通过以上例子,我们可以理解到什么是“聚集索引”和“非聚集索引”。进一步引申一下。

 

聚集索引

  聚集索引指的是数据表本身就是索引的一部分,就是指数据表本身就是聚集索引的子叶层,整个数据表的摆放顺序是按照你选定的键值由小到大排序,SQL SERVER  2000 之后的版本可指定数据由大到小排序。

  整个数据表按照键值字段由小到大排序,再搭配由键值字段加上指针的上层索引结构,也就是根节点和非子叶层级,形成整个聚集索引。因为数据表内实际摆放数据的方式只能遵循一种顺序,所以一个数据表只能有一个聚集索引。在指定聚集索引时,数据域本身并不需要唯一,或指定为唯一的聚集索引,SQL SERVER内部会自动为重复的键值建立4个字节的唯一标识。

  如果你的数据表有一列常常用来排序,另一列常常用来 范围查询,还有一列重复性非常高,则该用哪一列来做聚集索引。正确答案是依据哪个查询最重要,最常被用户执行。例如:你的老板一小时内多次执行某个查询当然比一个月执行一两次的查询来得重要。

  表(堆)创建聚集索引或删除和重新创建现有聚集索引时,要求数据库具有额外的可用工作区来容纳数据排序结果和原始表或现有聚集索引数据的临时副本。

  当堆或聚集表具有多个分区时,每个分区都有一个堆或 B 树结构,其中包含该指定分区的行组。例如,如果一个聚集表有 4 个分区,那么将有 4 个 B 树,每个分区一个。

 

  聚集索引( Clustered Index)

  ·        聚集索引的叶节点就是实际的数据页

  ·        在数据页中数据按照索引顺序存储

  ·        行的物理位置和行在索引中的位置是相同的

  ·        每个表只能有一个聚集索引

  ·        聚集索引的平均大小大约为表大小的 5%左右

 

  要使用索引来更有效地排序查询数据,最直接的方式就是在你要排序的字段上建立聚集索引。在建立聚集索引之后,SQL SERVER会重新组织数据页,让其中的数据行按照聚集索引中键值的顺序存储。SQL SERVER不需要在硬盘上的数据一定要实际按照聚集索引排序,但在建立聚集索引时,会尝试在逻辑上排序数据的同时,也会在物理上让数据尽可能地排序。在索引子叶层级中的每个数据页都有一个指针指向索引分页的前一页与后一页,形成双向链接串行,在内部的系统数据表包含了各索引子叶层第一个分页的地址,为了保证数据在逻辑上是依照聚集索引的顺序存放的,SQL SERVER 只需要由第一个分页开始,并依照其连接串行一个接着一个依序寻找数据即可。如下图。

 

        

 

注:聚集表是有聚集索引的表。

 

 

非聚集索引

   非聚集索引是完全独立于数据表之外的结构,所以不会影响数据行的顺序,其子叶层包含索引行。每个索引行包含非聚集键值、行定位符和任意包含列或非键列。行定位符中存入的数据有两种类型:书签(BOOKMARK)或聚集索引的键值。如果数据表上建立了聚集索引,则行定位符中存入的数据就是聚集索引的键值。如果数据表没有建立聚集索引,则行定位符中存入的数据就是书签,即指向数据表中记录具体位置的ROWID,也就是文档编号、分页编号与页内记录编号(称之为SOLT编号)所组合成的值。通过该ROWID 在数据表内获取数据就称为书签查找 BOOKMARK LOOKUP。所以,一般通过非聚集索引查找到符合的键值后,还会搭配书签查找。

  当非聚集索引从结构中找到符合的记录时,虽然在子叶层该键值是由小到大排序,因此可能在一个分页上就有全部符合查询条件的键值,但因为数据表中数据行的摆放是没有按顺序的(或是说没有按照该非聚集索引的键值顺序摆放),所以真正符合记录的数据是散布在文档各处的,而SQL SERVER每次读取数据都是以数据页为单位,因此,找到一条记录所在位置后,要先将存放该条记录的分页读到内存中,再从该页读出记录。

  因为BOOKMARK LOOKUP是进行随机的I/O操作,当符合查询的记录很多时,通过非聚集索引访问将导致数据页读取非常频繁,就算两条记录在同一个分页,该分页也会被重复读两次,因此或符合的记录有N条,就需要读取数据表内的分页N页,虽然大部分的读取操作都是针对内存中的高速缓存,但记录数过多时一样没有效率,还不如数据表扫描,全部扫描一遍,把符合条件数据找出来。

  虽然 SQL 2005 以后的版本中已经不在提 BOOKMARK LOOKUP了(但实际上却是换汤不换药),我们的很多搜索都是使用如下的搜索过程:先在非聚集中找,然后再在聚集索引中找。如下图。

        

 

 

  非聚集索引 ( Unclustered Index)  

  ·        非聚集索引的页,不是数据,而是指向数据页的页。

  ·        若未指定索引类型,则默认为非聚集索引

  ·        叶节点页的次序和表的物理存储次序不同

  ·        每个表最多可以有 249个非聚集索引(一般认为每个表不应该超过10个索引)

  ·        在非聚集索引创建之前创建聚集索引(否则会引发索引重建)

 

  聚集索引与非聚集索引使用的情况:

 动作描述

使用聚集索引 

 使用非聚集索引

 外键列

 应

 应

 主键列

 应

 应

 列经常被分组排序(order by)

 应

 应

 返回某范围内的数据

 应

 不应

 小数目的不同值

 应

 不应

 大数目的不同值

 不应

 应

 频繁更新的列

 不应 

 应

 频繁修改索引列

 不应

 应

 一个或极少不同值

 不应

 不应

 

  今天就普及一下索引的一些基本知识,明天来说明怎么选择要创建索引的列,条件是什么,方法是什么。

本文链接

相关 [sql server 性能优化] 推荐:

SQL Server 查询性能优化——堆表、碎片与索引(一)

- - 博客园_首页
      SQL Server在堆表中查询数据时,是不知道到底有多少数据行符合你所指定的查找条件,它将根据指定的查询条件把数据表的全部数据都查找一遍. 如果有可采用的索引,SQL Server只需要在索引层级查找每个索引分页的数据,再抓出所需要的少量数据分页即可. 访问数据表内数以万计的数据分页与只访问少数索引的分页两者间的差异,让索引变成效能调校的最佳工具.

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

- - 博客园_首页
索引是提高查询性能的一个重要工具,索引就是把查询语句所需要的少量数据添加到索引分页中,这样访问数据时只要访问少数索引的分页就可以. 但是索引对于提高查询性能也不是万能的,也不是建立越多的索引就越好. 索引建少了,用WHERE子句找数据效率低,不利于查找数据. 索引建多了,不利于新增、修改和删除等操作,因为做这些操作时,SQL SERVER除了要更新数据表本身,还要连带地立即更新所有的相关索引,而且过多的索引也会浪费硬盘空间.

SQL Server--索引

- - CSDN博客推荐文章
         1,概念:  数据库索引是对数据表中一个或多个列的值进行排序的结构,就像一本书的目录一样,索引提供了在行中快速查询特定行的能力..             2.1优点:  1,大大加快搜索数据的速度,这是引入索引的主要原因..                             2,创建唯一性索引,保证数据库表中每一行数据的唯一性..

SQL Server 面试

- - SQL - 编程语言 - ITeye博客
在SQL语言中,一个SELECT…FROM…WHERE语句称为一个查询块,将一个查询块嵌套在另一个查询块的WHERE子句中的查询称为子查询. 子查询分为嵌套子查询和相关子查询两种. 嵌套子查询的求解方法是由里向外处理,即每个子查询在其上一级查询处理之前求解,子查询的结果作为其父查询的查询条件. 子查询只执行一次,且可以单独执行;.

文章: SQL Server大负载的生产环境下的性能优化:初识元数据优化

- - InfoQ cn
相信朋友对SQL Server性能调优相关的知识或多或少都有一些了解. 虽然说现在NOSQL相关的技术非常的火热,但是RMDB(关系型数据库)与NOSQL是并存的,并且适用在各种的项目中. 在一般的企业级开发中,主要还是RMDB占据主导地位. 并且在互联网项目中,也不是摒弃了RMDB,例如MySQL就在很多的互联网应用中发挥着作用.

Oracle SQL性能优化

- - 数据库 - ITeye博客
(1)      选择最有效率的表名顺序(只在基于规则的优化器中有效):. ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表. 如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表.

SQL之性能优化

- - CSDN博客数据库推荐文章
在实际应用中,数据库中的数据会有很多,若要从这些数据表中检索数据,就需要对系统进行优化,提高数据库系统的响应速度,下面就是日常一些查询优化的方法. 索引可以提高数据库查询的速度,提高数据库的访问性能,但同时也会影响数据更新操作(例如插入、修改、删除)的速度. 如果WHERE子句中经常用到的某一列或者某几列创建索引.

Sql性能优化梳理

- - IT瘾-geek
本文主要针对的是关系型数据数据库MySql. 键值类数据库可以参考最简大数据Redis. 先简单梳理下Mysql的基本概念,然后分创建时和查询时这两个阶段的优化展开. 第一层:客户端通过连接服务,将要执行的sql指令传输过来. 第二层:服务器解析并优化sql,生成最终的执行计划并执行. 第三层:存储引擎,负责数据的储存和提取.

SQL Server优化50法

- - CSDN博客推荐文章
虽然查询速度慢的原因很多,但是如果通过一定的优化,也可以使查询问题得到一定程度的解决.   查询速度慢的原因很多,常见如下几种:没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷).   I/O吞吐量小,形成了瓶颈效应.   没有创建计算列导致查询不优化.   内存不足网络速度慢查询出的数据量过大(可以采用多次查询,其他的方法降低数据量).