首先明白为何索引会增长速度,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)列。若是要按姓查找特定职员,与必须搜索表中的全部行相比,索引会帮助您更快地得到该信息。
索引提供指向存储在表的指定列中的数据值的指针,而后根据您指定的排序顺序对这些指针排序。数据库使用索引的方式与您使用书籍中的索引的方式很类似:它搜索索引以找到特定值,而后顺指针找到包含该值的行。
在数据库关系图中,您能够在选定表的“索引/键”属性页中建立、编辑或删除每一个索引类型。当保存索引所附加到的表,或保存该表所在的关系图时,索引将保存在数据库中。有关详细信息,请参见建立索引。
注意;并不是全部的数据库都以相同的方式使用索引。有关更多信息,请参见数据库服务器注意事项,或者查阅数据库文档。
做为通用规则,只有当常常查询索引列中的数据时,才须要在表上建立索引。索引占用磁盘空间,而且下降添加、删除和更新行的速度。在多数状况下,索引用于数据检索的速度优点大大超过它的。
索引列
能够基于数据库表中的单列或多列建立索引。多列索引使您能够区分其中一列可能有相同值的行。
若是常常同时搜索两列或多列或按两列或多列排序时,索引也颇有帮助。例如,若是常常在同一查询中为姓和名两列设置判据,那么在这两列上建立多列索引将颇有意义。
肯定索引的有效性:
索引类型
根据数据库的功能,能够在数据库设计器中建立三种索引:惟一索引、主键索引和汇集索引。有关数据库所支持的索引功能的详细信息,请参见数据库文档。
提示:尽管惟一索引有助于定位信息,但为得到最佳性能结果,建议改用主键或惟一约束。
惟一索引是不容许其中任何两行具备相同索引值的索引。
当现有数据中存在重复的键值时,大多数数据库不容许将新建立的惟一索引与表一块儿保存。数据库还可能防止添加将在表中建立重复键值的新数据。例如,若是在 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