为什么建立了索引可以提高效率

  谈到sql优化,大家会异口同声的说建立索引,那么为什么建立了索引可以够提高效率?体现在哪?所有的查询都可以吗?什么样的查询才会提高效率?又有哪些注意事项呢?等等这一系列问题,下面让我们来一探究竟:

  一.为什么建立了索引可以够提高效率?体现在哪?

  先让我们看下

  (一)SQLS如何访问没有建立索引的数据表

  Heap译成汉语叫做“堆”,其本义暗含杂乱无章、无序的意思,前面提到数据值被写进数据页时,由于每一行记录之间并没有特定的排列顺序,所以行与行的顺序就是随机无序的,当然表中的数据页也就是无序的了,而表中所有数据页就形成了“堆”。可以说,一张没有索引的数据表,就像一个只有书柜而没有索引卡片柜的图书馆,书库里面塞满了一堆乱七八糟的图书。当读者对管理员提交查询请求后,管理员就一头钻进书库,对照查找内容从头开始一架一柜的逐本查找。运气好的话,在第一个书架的第一本书就  找到了,运气不好的话,要到最后一个书架的最后一本书才找到。
  SQLS在接到查询请求时,首先会分析sysindexes表中一个叫做索引标志符(INDID: Index ID)的字段的值,如果该值为0,表示这是一张数据表而不是索引表,SQLS就会使用sysindexes表的另一个字段FirstIAM值中找到该表的IAM页链,也就是所有数据页集合。
  这就是对一个没有建立索引的数据表进行数据查找的方式,是不是很没效率?对于没有索引的表,对于一“堆”这样的记录,SQLS也只能这样做,而且更没劲的是,即使在第一行就找到了被查询的记录,SQLS仍然要从头到尾的将表扫描一次。这种查询称为“遍历”,又叫“全表扫描”。
  可见没有建立索引的数据表照样可以运行,不过这种方法对于小规模的表来说没有什么太大的问题,但要查询海量的数据效率就太低了。

  (二)SQLS如何访问建立了非聚集索引的数据表
  非聚集索引可以建多个,具有B树结构,其叶级节点不包含数据页,只包含索引行。假定一个表中只有非聚集索引,则每个索引行包含了非聚集索引键值以及行定位符(ROW ID,RID),他们指向具有该键值的数据行,每一个RID由文件ID、页编号和在页中行的编号组成。
  当INDID的值在2至250之间时,意味着表中存在非聚集索引页。此时,SQLS调用ROOT字段的值指向非聚集索引B树的ROOT,在其中查找与被查询最相近的值,根据这个值找到在非叶级节点中的页号,然后顺藤摸瓜,在叶级节点相应的页面中找到该值的RID,最后根据这个RID在Heap中定位所在的页和行并返回到查询端。
  例如:假定在Lastname上建立了非聚集索引,则执行Select * From Member Where Lastname=’Ota’时,查询过程是:
  ①SQLS查询INDID值为2;
  ②立即从根出发,在非叶级节点中定位最接近Ota的值“Martin”,并查到其位于叶级页面的第61页;
  ③仅在叶级页面的第61页的Martin下搜寻Ota的RID,其RID显示为N∶706∶4,表示Lastname字段中名为Ota的记录位于堆的第706页的第4行,N表示文件的ID值,与数据无关;
  ④根据上述信息,SQLS立刻在堆的第706页第4行将该记录“揪”出来并显示于前台(客户端)。视表的数据量大小,整个查询过程费时从百分之几毫秒到数毫秒不等。
  在谈到索引基本概念的时候,我们就提到了这种方式:图书馆的前台有很多索引卡片柜,里面分了若干的类别,诸如按照书名笔画或拼音顺序、作者笔画或拼音顺序等,但有两点不同之处:
  ① 索引卡片上记录了每本书摆放的具体位置——位于某柜某架的第几本——而不是“特殊编号”;
  ② 书脊上并没有那个“特殊编号”。管理员在索引柜中查到所需图书的具体位置(RID)后,根据RID直接在书库中的具体位置将书提出来。
  显然,这种查询方式效率很高,但资源占用极大,因为书库中书的位置随时在发生变化,必然要求管理员花费额外的精力和时间随时做好索引更新。

  (三)SQLS如何访问建立聚集索引的数据表
  在聚集索引中,数据所在的数据页是叶级,索引数据所在的索引页是非叶级。
查询原理和上述对非聚集索引的查询相似,但由于记录是按照聚集索引中索引键值进行排序,换句话说,聚集索引的索引键值也就是具体的数据页。
  这就好比书库中的书就是按照书名的拼音在排序,而且也只按照这一种排序方式建立相应的索引卡片,于是查询起来要比上述只建立非聚集索引的方式要简单得多。仍以上面的查询为例:
  假定在Lastname字段上建立了聚集索引,则执行Select * From Member Where Lastname=’Ota’时,查询过程是:
  ①SQLS查询INDID值为1,这是在系统中只建立了聚集索引的标志;
  ②立即从根出发,在非叶级节点中定位最接近Ota的值“Martin”,并查到其位于叶级页面的第120页;
  ③在位于叶级页面第120页的Martin下搜寻到Ota条目,而这一条目已是数据记录本身;
  ④将该记录返回客户端。
  这一次的效率比第二种方法更高,以致于看起来更美,然而它最大的优点也恰好是它最大的缺点——由于同一张表中同时只能按照一种顺序排列,所以在任何一种数据表中的聚集索引只能建立一个;并且建立聚集索引需要至少相当于源表120%的附加空间,以存放源表的副本和索引中间页。
  难道鱼和熊掌就不能兼顾了吗?办法是有的。

  (四)SQLS如何访问既有聚集索引、又有非聚集索引的数据表
  如果我们在建立非聚集索引之前先建立了聚集索引的话,那么非聚集索引就可以使用聚集索引的关键字进行检索。就像在图书馆中,前台卡片柜中可以有不同类
别的图书索引卡,然而每张卡片上都载明了那个特殊编号——并不是书籍存放的具体位置。这样在最大程度上既照顾了数据检索的快捷性,又使索引的日常维护变得
更加可行,这是最为科学的检索方法。
  也就是说,在只建立了非聚集索引的情况下,每个叶级节点指明了记录的行定位符(RID);而在既有聚集索引又有非聚集索引的情况下,每个叶级节点所指向的是该聚集索引的索引键值,即数据记录本身。
假设聚集索引建立在Lastname上,而非聚集索引建立在Firstname上,当执行Select * From Member Where Firstname=’Mike’时,查询过程是:
  ①SQLS查询INDID值为2;
  ②立即从根出发,在Firstname的非聚集索引的非叶级节点中定位最接近Mike的值“Jose”条目;
  ③从Jose条目下的叶级页面中查到Mike逻辑位置——不是RID而是聚集索引的指针;
  ④根据这一指针所指示位置,直接进入位于Lastname的聚集索引中的叶级页面中到达Mike数据记录本身;
  ⑤将该记录返回客户端。
  这就完全和我们在“索引的基本概念”中讲到的现实场景完全一样了,当数据发生更新的时候,SQLS只负责对聚集索引的键值加以维护,而不必考虑非聚集
索引。只要我们在ID类的字段上建立聚集索引,而在其它经常需要查询的字段上建立非聚集索引,通过这种科学的、有针对性的在一张表上分别建立聚集索引和非
聚集索引的方法,我们既享受了索引带来的灵活与快捷,又相对避免了维护索引所导致的大量的额外资源消耗。 

  二.所有的查询都可以吗?什么样的查询才会提高效率?

  其实,建立索引后并不是所有的查询都会提高效率,当且仅当基于“被建立”索引的列时进行查询时,才有可能会提高效率(后面提为什么要加上可能二字)

  三.索引的优缺点及注意事项

  1.优势:基于该列的查询效率高,这个毋庸置疑

  缺点:空间占用,插入时效率低,可以从如下几个方面考虑:
  1.1、系统要占用大约为表的1.2倍的硬盘和内存空间来保存索引;空间
  1.2、更新数据的时候,系统必须要有额外的时间来同时对索引进行更新,以维持数据和索引的一致性。插入效率
  
  2.注意事项:
  2.1、不应选取很少或从不引用的字段;尽量选取在经常需要根据某个列进行查询的某个列。
  2.2、 选取的列不超过总数的10%,不要建在逻辑型的字段,如男或女(是或否)等上。

  2.3、主键默认创建索引。

  2.4、基于函数的索引,要建立函数索引,否则会导致索引失效,全表扫描,如下例:

  create index i_account_real_name on account(real_name)

当我们运行select id,real_name from account where upper(real_name)=?时,便使索引失效,要想使用函数,要按如下方法建立函数索引

  create index i_account_real_name on account(upper(real_name))

时间: 2024-12-29 09:51:55

为什么建立了索引可以提高效率的相关文章

mysql使用索引优化查询效率

索引的概念 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针.更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度.在没有索引的情况下,数据库会遍历全部数据后选择符合条件的:而有了相应的索引之后,数据库会直接在索引中查找符合条件的选项.如果我们把SQL语句换成"SELECT * FROM 表名 WHERE id=2000000",那么你是希望数据库按照顺序读取完200万行数据以后给你结果还是直接在索引中定位

SQL应用与开发:(九)提高效率的索引

在数据库中,索引是一个特殊的对象,是一种可以加快数据检索的数据库结构,它可以从大量的数据中迅速找到需要的内容,使得数据查询时不必检索整个数据库.索引是一种基于表中数据的对象,与视图不同,索引需要占用物理存储.使用数据库的索引,使我们能够较快的查询数据. 1.简介 索引是表示数据的一种方式,它提供的数据顺序不用于数据在磁盘上的物理存储顺序.索引基于表的一列或多列组合建立,在表内重新排列记录的物理位置.当使用索引时,数据是以分类排序的方式提供给用户的,排列顺序可以用创建索引语句控制.通常,通过在正确

26、蛤蟆的数据结构笔记之二十六串应用之建立词索引表

26.蛤蟆的数据结构笔记之二十六串应用之建立词索引表 本篇名言:"生命是一条美丽而曲折的幽径,路旁有妍花的丽蝶,累累的美果,但我们很少去停留观赏,或咀嚼它,只一心一意地渴望赶到我们幻想中更加美丽的豁然开朗的大道.然而在前进的程途中,却逐渐树影凄凉,花蝶匿迹,果实无存,最后终于发觉到达一个荒 漠.-- 萨拉" 1.  信息检索 信息检索是计算机应用的重要领域之一.为了提高图书馆数目检索的效率,建立书名关键词索引,可以实现读者快速检索书目的自动化,即读者根据关键词索引表,读者可以方便查询到

表压缩及并行提高效率的测试

1.制作测试表 create table t1 as select * from FW_T_GTXLOG insert into t1 select * from t1; create table t2 compress as select * from t1 create table t3 as select * from t1 2.查看测试表数据量 select count(*) from t1; select count(*) from t2; select count(*) from t

HBase整合MapReduce之建立HBase索引

HBase索引主要用于提高Hbase中表数据的访问速度,有效的避免了全表扫描,HBase中的表根据行健被分成了多个Regions,通常一个region的一行都会包含较多的数据,如果以列值作为查询条件,就只能从第一行数据开始往下找,直到找到相关数据为止,这很低效.相反,如果将经常被查询的列作为行健.行健作为列重新构造一张表,即可实现根据列值快速定位相关数据所在的行,这就是索引.显然索引表仅需要包含一个列,所以索引表的大小和原表比起来要小得多,如图4-14给出了索引表与原表之间的关系.从图可以看出,

处理百万级以上的数据查询提高效率的办法

1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from

sql的简单提高效率方法

少用in操作(效率极差),尽量用表关联代替 尽量有where(减少读取量),where操作列尽量有索引(加快查询) (mysql索引使用B-Tree数据结构对特定列额外组织存放,加快存储引擎查找记录的速度,不需回表查询数据的就是聚簇索引(索引和数据存放在一起).通常是需要回表再查数据,需要消耗额外的磁盘IO.) 主键是特殊的唯一索引(不含null),唯一索引更好用 复合索引设计合理,比多列索引强.因为多列索引在where中引用时,列顺序非常重要,要满足最左前缀列,左边优先,不一定能构建合理的索引

建立高性能索引

(1)隔离列 如果在查询中没有隔离索引的列,mysql通常不会使用索引."隔离"列意味着它不是表达式的一部分,也没有位于函数中. 如以下的查询将不会使用actor_id上的索引 1 mysql>select * from actor where actor_id +1 = 5 我们虽然很容易的看出actor_id=4但是mysql却不会帮你解方程. 例如 1 mysql>select ... wher date_col >=TO_DAYS(CURRENT_DATE)

为什麽我们一般会在自增列或交易时间列上建立聚集索引?

http://www.cnblogs.com/lyhabc/p/3533027.html 一般的交易系统里面我们都会以自增列或交易时间列作为聚集索引列,因为一般这些系统都是写多读少 每天的交易数据会不停的插入到数据库,但是读取数据就没有数据插入那么频繁 因为这些系统一般是写多读少,所以我们会选择在自增列或交易时间列上建立聚集索引 测试 测试环境:SQLSERVER2012 SP1  WINDOWS7 64位 我们来做一个测试,测试脚本如下: 1 --测试脚本 插入性能 2 USE [test]