数据库创建索引为何会加快查询速度

首先明白为何索引会增长速度,DB在执行一条Sql语句的时候,默认的方式是根据搜索条件进行全表扫描,遇到匹配条件的就加入搜索结果集合。若是咱们对某一字段增长索引,查询时就会先去索引列表中一次定位到特定值的行数,大大减小遍历匹配的行数,因此能明显增长查询的速度html

    添加索引的话,首先去索引列表中查询,而咱们的索引列表是B类树的数据结构,查询的时间复杂度为O(log2N),定位到特定值得行就会很是快,因此其查询速度就会很是快。sql

为何说B+-tree比B 树更适合实际应用中操做系统的文件索引和数据库索引?数据库

1) B+-tree的磁盘读写代价更低安全

B+-tree的内部结点并无指向关键字具体信息的指针。所以其内部结点相对B 树更小。若是把全部同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的须要查找的关键字也就越多。相对来讲IO读写次数也就下降了。服务器

    举个例子,假设磁盘中的一个盘块容纳16bytes,而一个关键字2bytes,一个关键字具体信息指针2bytes。一棵9阶B-tree(一个结点最多8个关键字)的内部结点须要2个盘快。而B树内部结点只须要1个盘快。当须要把内部结点读入内存中的时候,B 树就比B树多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。数据结构

2) B+-tree的查询效率更加稳定oracle

因为非终结点并非最终指向文件内容的结点,而只是叶子结点中关键字的索引。因此任何关键字的查找必须走一条从根结点到叶子结点的路。全部关键字查询的路径长度相同,致使每个数据的查询效率至关。数据库设计

读者点评
fanyy1991(csdn用户名)道:我的以为这两个缘由都不是主要缘由。数据库索引采用B+树的主要缘由是 B树在提升了磁盘IO性能的同时并无解决元素遍历的效率低下的问题。正是为了解决这个问题,B+树应运而生。B+树只要遍历叶子节点就能够实现整棵树的遍历。并且在数据库中基于范围的查询是很是频繁的,而B树不支持这样的操做(或者说效率过低)。函数

上述那个问题转载自:从B树、B+树、B*树谈到R 树post

    那么在任什么时候候都应该加索引么?这里有几个反例:一、若是每次都须要取到全部表记录,不管如何都必须进行全表扫描了,那么是否加索引也没有意义了。二、对非惟一的字段,例如“性别”这种大量重复值的字段,增长索引也没有什么意义。三、对于记录比较少的表,增长索引不会带来速度的优化反而浪费了存储空间,由于索引是须要存储空间的,并且有个致命缺点是对于update/insert/delete的每次执行,字段的索引都必须从新计算更新。

    那么在何时适合加上索引呢?咱们看一个Mysql手册中举的例子,这里有一条sql语句:

    SELECT c.companyID, c.companyName FROM Companies c, User u WHERE c.companyID = u.fk_companyID AND c.numEmployees >= 0 AND c.companyName LIKE '%i%' AND u.groupID IN (SELECT g.groupID FROM Groups g WHERE g.groupLabel = 'Executive')

    这条语句涉及3个表的联接,而且包括了许多搜索条件好比大小比较,Like匹配等。在没有索引的状况下Mysql须要执行的扫描行数是 77721876行。而咱们经过在companyID和groupLabel两个字段上加上索引以后,扫描的行数只须要134行。在Mysql中能够经过 Explain Select来查看扫描次数。能够看出来在这种联表和复杂搜索条件的状况下,索引带来的性能提高远比它所占据的磁盘空间要重要得多。

    那么索引是如何实现的呢?大多数DB厂商实现索引都是基于一种数据结构——B树。oracle实现索引的数据结构是B*树。具体关于B树、B+树、B*树的讲解能够查看另外一篇博文:

    能够看到在这棵B树搜索英文字母复杂度只为o(m),在数据量比较大的状况下,这样的结构能够大大增长查询速度。然而有另一种数据结构查询的虚度比B树更快——散列表。Hash表的定义是这样的:设全部可能出现的关键字集合为u,实际发生存储的关键字记为k,而|k|比|u|小不少。散列方法是经过散列函数h将u映射到表T[0,m-1]的下标上,这样u中的关键字为变量,以h为函数运算结果即为相应结点的存储地址。从而达到能够在o(1)的时间内完成查找。
    然而散列表有一个缺陷,那就是散列冲突,即两个关键字经过散列函数计算出了相同的结果。设m和n分别表示散列表的长度和填满的结点数,n/m为散列表的填装因子,因子越大,表示散列冲突的机会越大。
    由于有这样的缺陷,因此数据库不会使用散列表来作为索引的默认实现,Mysql宣称会根据执行查询格式尝试将基于磁盘的B树索引转变为和合适的散列索引以追求进一步提升搜索速度。我想其它数据库厂商也会有相似的策略,毕竟在数据库战场上,搜索速度和管理安全同样是很是重要的竞争点。

 

基本概念介绍:

 

索引

使用索引可快速访问数据库表中的特定信息。索引是对数据库表中一列或多列的值进行排序的一种结构,例如 employee 表的姓(lname)列。若是要按姓查找特定职员,与必须搜索表中的全部行相比,索引会帮助您更快地得到该信息。

索引提供指向存储在表的指定列中的数据值的指针,而后根据您指定的排序顺序对这些指针排序。数据库使用索引的方式与您使用书籍中的索引的方式很类似:它搜索索引以找到特定值,而后顺指针找到包含该值的行。

在数据库关系图中,您能够在选定表的“索引/键”属性页中建立、编辑或删除每一个索引类型。当保存索引所附加到的表,或保存该表所在的关系图时,索引将保存在数据库中。有关详细信息,请参见建立索引。

注意;并不是全部的数据库都以相同的方式使用索引。有关更多信息,请参见数据库服务器注意事项,或者查阅数据库文档。

做为通用规则,只有当常常查询索引列中的数据时,才须要在表上建立索引。索引占用磁盘空间,而且下降添加、删除和更新行的速度。在多数状况下,索引用于数据检索的速度优点大大超过它的。

索引列

能够基于数据库表中的单列或多列建立索引。多列索引使您能够区分其中一列可能有相同值的行。

若是常常同时搜索两列或多列或按两列或多列排序时,索引也颇有帮助。例如,若是常常在同一查询中为姓和名两列设置判据,那么在这两列上建立多列索引将颇有意义。

肯定索引的有效性:

  • 检查查询的 WHERE 和 JOIN 子句。在任一子句中包括的每一列都是索引能够选择的对象。
  • 对新索引进行试验以检查它对运行查询性能的影响。
  • 考虑已在表上建立的索引数量。最好避免在单个表上有不少索引。
  • 检查已在表上建立的索引的定义。最好避免包含共享列的重叠索引。
  • 检查某列中惟一数据值的数量,并将该数量与表中的行数进行比较。比较的结果就是该列的可选择性,这有助于肯定该列是否适合创建索引,若是适合,肯定索引的类型。

索引类型

根据数据库的功能,能够在数据库设计器中建立三种索引:惟一索引、主键索引和汇集索引。有关数据库所支持的索引功能的详细信息,请参见数据库文档。

提示:尽管惟一索引有助于定位信息,但为得到最佳性能结果,建议改用主键或惟一约束。

惟一索引

惟一索引是不容许其中任何两行具备相同索引值的索引。

当现有数据中存在重复的键值时,大多数数据库不容许将新建立的惟一索引与表一块儿保存。数据库还可能防止添加将在表中建立重复键值的新数据。例如,若是在 employee 表中职员的姓 (lname) 上建立了惟一索引,则任何两个员工都不能同姓。

主键索引

数据库表常常有一列或列组合,其值惟一标识表中的每一行。该列称为表的主键。

在数据库关系图中为表定义主键将自动建立主键索引,主键索引是惟一索引的特定类型。该索引要求主键中的每一个值都惟一。当在查询中使用主键索引时,它还容许对数据的快速访问。

汇集索引

在汇集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个汇集索引。

若是某索引不是汇集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非汇集索引相比,汇集索引一般提供更快的数据访问速度。

 

创建方式和注意事项

最普通的状况,是为出如今where子句的字段建一个索引。为方便讲述,咱们先创建一个以下的表。 

CREATE TABLE mytable ( 

 id serial primary key, 

 category_id int not null default 0, 

 user_id int not null default 0, 

 adddate int not null default 0 

); 

若是你在查询时经常使用相似如下的语句: 

 SELECT * FROM mytable WHERE category_id=1; 

最直接的应对之道,是为category_id创建一个简单的索引: 

 CREATE INDEX mytable_categoryid 

 ON mytable (category_id); 

OK.若是你有不止一个选择条件呢?例如: 

 SELECT * FROM mytable WHERE category_id=1 AND user_id=2; 

你的第一反应多是,再给user_id创建一个索引。很差,这不是一个最佳的方法。你能够创建多重的索引。 

CREATE INDEX mytable_categoryid_userid ON mytable (category_id,user_id); 

注意到我在命名时的习惯了吗?我使用"表名_字段1名_字段2名"的方式。你很快就会知道我为何这样作了。 

如今你已经为适当的字段创建了索引,不过,仍是有点不放心吧,你可能会问,数据库会真正用到这些索引吗?测试一下就OK,对于大多数的数据库来讲,这是很容易的,只要使用EXPLAIN命令: 

EXPLAIN 

 SELECT * FROM mytable 

WHERE category_id=1 AND user_id=2; 

 This is what Postgres 7.1 returns (exactly as I expected) 

 NOTICE: QUERY PLAN: 

 Index Scan using mytable_categoryid_userid on 

 mytable (cost=0.00..2.02 rows=1 width=16) 

EXPLAIN 

以上是postgres的数据,能够看到该数据库在查询的时候使用了一个索引(一个好开始),并且它使用的是我建立的第二个索引。看到我上面命名的好处了吧,你立刻知道它使用适当的索引了。

接着,来个稍微复杂一点的,若是有个ORDER BY字句呢?无论你信不信,大多数的数据库在使用order by的时候,都将会从索引中受益。 

 SELECT * FROM mytable 

WHERE category_id=1 AND user_id=2 

 ORDER BY adddate DESC;

很简单,就象为where字句中的字段创建一个索引同样,也为ORDER BY的字句中的字段创建一个索引: 

 CREATE INDEX mytable_categoryid_userid_adddate 

 ON mytable (category_id,user_id,adddate); 

 注意: "mytable_categoryid_userid_adddate" 将会被截短为 

"mytable_categoryid_userid_addda" 

 CREATE 

 EXPLAIN SELECT * FROM mytable 

WHERE category_id=1 AND user_id=2 

 ORDER BY adddate DESC; 

 NOTICE: QUERY PLAN: 

 Sort (cost=2.03..2.03 rows=1 width=16) 

-> Index Scan using mytable_categoryid_userid_addda 

 on mytable (cost=0.00..2.02 rows=1 width=16) 

 EXPLAIN 

看看EXPLAIN的输出,数据库多作了一个咱们没有要求的排序,这下知道性能如何受损了吧,看来咱们对于数据库的自身运做是有点过于乐观了,那么,给数据库多一点提示吧。 

为了跳过排序这一步,咱们并不须要其它另外的索引,只要将查询语句稍微改一下。这里用的是postgres,咱们将给该数据库一个额外的提示--在 ORDER BY语句中,加入where语句中的字段。这只是一个技术上的处理,并非必须的,由于实际上在另外两个字段上,并不会有任何的排序操做,不过若是加入,postgres将会知道哪些是它应该作的。 

 EXPLAIN SELECT * FROM mytable 

WHERE category_id=1 AND user_id=2 

 ORDER BY category_id DESC,user_id DESC,adddate DESC; 

 NOTICE: QUERY PLAN: 

 Index Scan Backward using 

mytable_categoryid_userid_addda on mytable 

 (cost=0.00..2.02 rows=1 width=16) 

 EXPLAIN 

如今使用咱们料想的索引了,并且它还挺聪明,知道能够从索引后面开始读,从而避免了任何的排序。

以上说得细了一点,不过若是你的数据库很是巨大,而且每日的页面请求达上百万算,我想你会获益良多的。不过,若是你要作更为复杂的查询呢,例如将多张表结合起来查询,特别是where限制字句中的字段是来自不止一个表格时,应该怎样处理呢?我一般都尽可能避免这种作法,由于这样数据库要将各个表中的东西都结合起来,而后再排除那些不合适的行,搞很差开销会很大。 

若是不能避免,你应该查看每张要结合起来的表,而且使用以上的策略来创建索引,而后再用EXPLAIN命令验证一下是否使用了你料想中的索引。若是是的话,就OK。不是的话,你可能要创建临时的表来将他们结合在一块儿,而且使用适当的索引。 

要注意的是,创建太多的索引将会影响更新和插入的速度,由于它须要一样更新每一个索引文件。对于一个常常须要更新和插入的表格,就没有必要为一个不多使用的where字句单独创建索引了,对于比较小的表,排序的开销不会很大,也没有必要创建另外的索引。 

以上介绍的只是一些十分基本的东西,其实里面的学问也很多,单凭EXPLAIN咱们是不能断定该方法是否就是最优化的,每一个数据库都有本身的一些优化器,虽然可能还不太完善,可是它们都会在查询时对比过哪一种方式较快,在某些状况下,创建索引的话也未必会快,例如索引放在一个不连续的存储空间时,这会增长读磁盘的负担,所以,哪一个是最优,应该经过实际的使用环境来检验。 

在刚开始的时候,若是表不大,没有必要做索引,个人意见是在须要的时候才做索引,也可用一些命令来优化表,例如MySQL可用"OPTIMIZE TABLE"。 

综上所述,在如何为数据库创建恰当的索引方面,你应该有一些基本的概念了。

 

转载自:http://www.jb51.net/article/27315.htm

相关文章
相关标签/搜索