一 、引言html
首先咱们来思考一下什么是索引?索引的做用是什么?操做系统的文件索引和数据库的索引有什么不一样?mysql
什么是索引?对于这个问题咱们能够打一个比喻,索引相对于文件的做用,就比如是目录相对于一本书的做用。因此它的做用也就显而易见了,就是为了查找,提升查找效率。是否是感受不太有用,那再想想你查字典的时候一页一页的找试一试,买一本最便宜的字典都要含着泪才能翻完。正常查找字典咱们通常先找到部首的笔画,而后找到部首,再根据部首找到字,再根据字找到对应的页,这其实就是一个多级索引。因此说计算机科学里面的不少智慧来来自于生活。算法
接下来就是操做系统的文件索引和数据库的索引的区别了。通常操做系统都有一张索引表,由于通常操做系统的文件是无结构的字节系列,因此操做系统的索引表记录的是数据的逻辑块号和对应的物理块号。而数据库文件是有结构的记录,因此它能够由每一条记录的关键码来和物理块对应。sql
特别须要注意的是索引键对于的值是磁盘(外存)的物理地址,而不是内存中的逻辑地址。数据库在读取表的时候首先是先读取索引文件(可能数据文件自己就是索引文件,这和不一样的实现方式相关,InnoDB就是这种实现)。而后根据索引表来读取数据。
数据库
二 、选择率性能
要理解利用索引对数据库查询作优化有一点很是重要,就是全表扫描和索引扫描的区别,索引下面的内容很是重要。优化
对数据库操做影响最大的就是IO操做。表的扫描操做就和IO密切相关。由于数据库通常都会经过操做系统的IO,操做系统中读取数据用两种很是重要方式:ui
顺序读取:就是读取磁盘上连续的块,速度快spa
随机读取:随机读是指访问的块是不连续的,须要磁盘的磁头不断移动。随机读的性能是远远低于顺序读的。操作系统
矛盾的问题来了,索引扫描是随机读取的,而全表扫描是顺序读取的。这明显不科学啊!明明咱们利用索引是来作优化的。为何用慢的随机读取呢?一个很重要的数据就是选择率,了解它你就会发现不少其余书籍或者文章中提到的利用索引优化的限制和技巧的缘由了。
常常看见提醒要作有where条件上的列加上索引,为何呢?where就意味着过滤,不少时候过滤意味着咱们其实须要查询的数据量很小,例如登陆时候的用户验证:
select 1 from user where username=’tom’ and password=sha1(’123456’)
咱们只须要查找一条记录,若是咱们有1亿条记录,若是作全表扫描按一半来算平均也要扫描5千万条记录。这须要多少次IO操做啊?而索引扫描只须要读取一条记录,基本上一次IO就能够搞定。因此当选择率很低的时候,就算是用索引的随机读取比顺序读取慢不少,可是也会比全表扫描效率高不少。
还有一些优化技巧提醒你在取值范围小的列上不要创建索引,也是由于选择率的问题。例如在性别字段上创建一个索引,而后读取数据:
select * from user where sex=’male’;
咱们按男女比例均衡来算,至少也要选择通常的数据吧,就算加上一些特殊人群,也是几分之一的几率吧,若是数据量大,还不是就哭了。因此数据库的优化器会在执行查询计划的时候就会计算选择率,若是选择率太高,就直接放弃索引扫描,改用全表扫描。由于咱们前面提过全表扫描用的顺序读取比索引扫描用的随机读取速度快。明显选择率高的时候咱们读的数据多,用顺序读取的优点要大于用随机读取。
三 、Hash索引和B+数索引
Hash索引相对比较简单,就是利用hash表来记录列的值和对应列存储的物理地址。它的效率和hash算法相关。
B+树的B表明的是Balance(平衡)而不是Binary(二叉),用B+树实现的 索引也不能定位到数据项,只能定位到页(见注1),这和B+树特色和实现相关,B+树也是树,也有节点,而MySQL数据库把一个节点的大小设置为一页,这也一次就能够读取一个节点。它有这很好的查询效率比O(n)好。
关于hash索引和B+树索引,若是你感兴趣能够参考后面给出的参考列表,这里就不作详细介绍了,下图是我截取的一张为何有hash索引还要B+树索引的缘由,由于认为可能你会用到,可是为了防止链接失效,因此截取了一张图。后面有原文链接,若是你感兴趣能够参考原文。
图1:为何还须要B+树索引
四 、实例
如今仍是不太明显可以感觉到索引对于数据库效率的影响是吗?不要紧,咱们用一个实例来分析一下使用索引和不使用索引的区别,而后对比一下使用索引的性能和不使用索引的性能差别。
假设咱们有一张user表以下所示:
图2:user表
user表的建立语句:
CREATE TABLE IF NOT EXISTS `user` ( `id` INT NOT NULL , `name` CHAR(30) NOT NULL , `phone` CHAR(11) NULL , `address` VARCHAR(50) NULL , `password` CHAR(40) NOT NULL , `description` TEXT NULL , PRIMARY KEY (`id`) )ENGINE = InnoDB;
user表只有一个主键索引,是数据库默认为主键加上的。咱们理一下如今当咱们想user表插入插入数据和查询数据时数据库是怎么作的。
插入一条记录时,数据库首先根据索引列id生成一个值做为索引的键值,把记录存放的物理地址做存放在相应的表记录中。索引表与数据存储在物理存储上的关系以下图2。
图3:数据库索引使用示意图
这也体现了索引的负效应,就是要维护索引表,当插入和删除记录的时候,要跟新索引表,这就带来了消耗。注意图2只是一个示意图,索引的结构并不必定是这样。
咱们在来看查询数据,当咱们只查询索引列的时候,就会使用索引扫描。不然使用顺序扫描读取。在MySQL中咱们能够用explain语句来验证一下:
图4:只查询索引
图5:只查询非索引列
图6:同时查询索引列和非索引列
咱们看到了只有当咱们查询索引列的时候才使用了索引,为何都是一条记录,可是只有索引列才使用索引呢?这样和前文提到的顺序读取和随机读取的效率有关。
最后,若是能够在表中插入几百万条数据,而后来验证一下一样选择一行数据,利用索引扫描和利用全表扫描的效率差异。
五 、相关的参考
操做系统的IO管理介绍:http://www.94cto.com/index/Article/content/id/748.html
磁盘IO参数相关:http://storage.it168.com/a2011/0323/1169/000001169755.shtml
B+树相关:http://blog.sina.com.cn/s/blog_4e0c21cc01010itp.html
Hash索引和B+树索引:
http://dev.mysql.com/doc/refman/5.6/en/index-btree-hash.html(官方文档)
http://blog.sina.com.cn/s/blog_6776884e0100pko1.html
操做系统分页相关:http://blog.chinaunix.net/uid-28458801-id-3505434.html
操做系统文件读取:http://blog.csdn.net/hguisu/article/details/6120991
理解B+树算法和Innodb索引:http://www.ruzuojun.com/topic/420.html
注1:这里的页是指操做系统的页面大小,为何要分页呢?简单的说就是咱们的物理内存是有限的,咱们要利用虚拟内存(磁盘等外存)。当咱们的物理内存不够用的时候,就会把物理内存中的一些数据置换到虚拟内存中,可是置换多大的内存呢?为了方便管理,操做系统一般作法就是对内存就行分页,按页置换。读取数据时,一个块的大小通常也是一个页的大小。