咱们在学习MySQL的时候常常会听到索引这个词,大概也知道这是什么,可是深究下去又说不出什么道道来。下面将会比较全面的介绍一下关于索引!html
这里用百度百科的一句话来讲,在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。mysql
简单来讲,索引就是咱们一本书的目录,经过目录咱们才能更快在一本书中查找到咱们所要看的内容。一样的,经过索引咱们才能在数据库中查找到咱们的数据!git
咱们知道索引能够加快咱们的查找,因此这里经过没有使用索引的查找能够更加地让咱们认识到使用索引的好处。github
咱们的MySQL基本的页存储结构是页,也就是咱们的数据记录都在页里面。算法
当咱们插入一条记录的时候就会存储在咱们的数据页中的存放行记录的位置,并在咱们的Page Directory页目录那里生成主键的信息。咱们的数据页中记录又能够组成一个单链表,每插入一条数据就会在尾节点那里添加上。sql
当咱们经过主键查找某条记录的时候能够在页目录中使用二分法快速定位到对应的槽,而后再遍历该槽对应分组中的记录便可快速找到指定的记录。若是不是主键的话,那么只能遍历单链表中的每条记录对比查找。数据库
因此,若是不用索引优化的话,那么在进行一条查找的sql的话,默认的流程是这样子的:服务器
若是在数据量特别大的时候,又是极端状况,遍历双向链表和单链表,速度就会显得很是慢!数据结构
当人们开始谈论索引的时候,若是没有特别指明类型的话,那么多半说的就是B-Tree(B树)因此,它使用的是B-Tree数据结构来存储数据,大多数的MySQL引擎都支持这种索引(但实际上不少存储引擎使用的是B+Tree,这个咱们稍后再谈到)。咱们这里经过B-Tree索引结构就能够极大的优化了上面的查找。性能
从图中咱们能够很明显的感觉到,使用索引后,就不须要再遍历双向链表那样去查找页,而是经过目录就能够很快的定位到咱们的实际的页。如查找id为1的数据
并且,咱们也能够根据图总结出B-Tree的特色:
B+Tree索引是依据B-Tree索引基础上的一次优化,具体变化以下:
B+Tree 非叶子节点不存放数据
叶子节点存储关键字和数据,非叶子节点的关键字也会沉到叶子节点,而且排序
叶子节点两两指针相互链接,造成一个双向环形链表(符合磁盘的预读特性),顺序查询性能更高(区间查找更加方便)
咱们的B+Tree的优化到底有什么好处呢?
首先是咱们的数据只放在了叶子节点上面。这个惟一的好处就是咱们的非叶子节点能够存放更多的关键字了,总体就能够存放更多的数据。由于咱们MySQL查询过程是按页加载数据的,每加载一页就是一次IO操做,咱们根磁盘页存放的数据越少,关键字越多,那么总体的数据量就能够说是越多。
还有一个好处就是,在叶子节点造成双向环形链表。这样子若是要进行区间查询的话,只须要顺着叶子节点的指针向下查询就行。而若是是B-Tree的话,就须要返回上一级节点而后再读取磁盘页进行查找,节省了很多时间!
为何要采用B-Tree的结构,甚至是B+Tree的结构呢?
其实仍是跟咱们的磁盘读取的缘由有关。上面咱们说到了,MySQL查询过程是按页加载数据的。而咱们的操做系统通常将内存和磁盘分割成固定大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。咱们的内存每次读取的就是MySQL分割成的一个页大小,也就是一个索引点,图中的一个磁盘页。
采用普通二叉树?不!
若是采用普通的二叉树的话,咱们要考虑到一种状况。那就是在极端的状况下,一棵树是会退化成链表的,那么树的优势就没有了。 这与咱们原来用双向链表有何异同?
采用红黑树?不!
那么可能有人说了,若是采用红黑树,树保持平衡不就好了吗?确实,红黑树等平衡树也能够用来实现索引,可是与咱们的B-Tree/B+Tree来讲性能要差不少。
咱们上面说到了内存每一次I/O都是载入一个索引节点,也就是一个磁盘页。若是数据不在同一个磁盘块上,那么一般须要移动制动手臂进行寻道,而制动手臂由于其物理结构致使了移动效率低下,从而增长磁盘数据读取时间。B-Tree/B+Tree相对于红黑树有更低的树高,进行寻道的次数与树高成正比,在同一个磁盘块上进行访问只须要很短的磁盘旋转时间,因此 B-Tree/B+Tree 树更适合磁盘数据的读取。
并且为了减小磁盘 I/O 操做,磁盘每每不是严格按需读取,而是每次都会预读。预读过程当中,磁盘进行顺序读取,顺序读取不须要进行磁盘寻道,而且只须要很短的磁盘旋转时间,速度会很是快。而且能够利用预读特性,相邻的节点也可以被预先载入。
MySQL除了B+树以外,还有一种常见的是就是哈希索引。
哈希索引就是采用必定的哈希算法,把键值换算成新的哈希值,检索时不须要相似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法便可马上定位到相应的位置,速度很是快。
本质上就是把键值换算成新的哈希值,根据这个哈希值来定位。
使用哈希索引最大的好处就是速度特别快,咱们只须要一次定位就能够找到咱们要的数据。时间复杂度为O(1),可是咱们的InnoDB(MySQL默认存储引擎)默认使用的倒是B+树索引,这也是由于哈希索引有必定的缺点:
但是若是一个索引值被频繁使用的话,咱们的InnoDB会再B+Tree索引之上再建立一个哈希索引,用来方便快速查找。这个功能叫作“自适应哈希索引”。
MySQL数据库中innodb存储引擎,B+树索引能够分为聚簇索引(也称汇集索引,clustered index)和辅助索引(有时也称非聚簇索引或二级索引,secondary index,non-clustered index)。这两种索引内部都是B+树,汇集索引的叶子节点存放着一整行的数据。
Innodb中的主键索引是一种聚簇索引,非聚簇索引都是辅助索引,像复合索引、前缀索引、惟一索引。
聚簇索引就是按照每张表的主键构造一颗B+树,同时叶子节点中存放的就是整张表的行记录数据,也将汇集索引的叶子节点称为数据页。这个特性决定了索引组织表中数据也是索引的一部分,每张表只能拥有一个聚簇索引。
Innodb经过主键汇集数据,若是没有定义主键,innodb会选择非空的惟一索引代替。若是没有这样的索引,innodb会隐式的定义一个主键来做为聚簇索引。
使用聚簇索引的优势:
缺点:
Innodb中聚簇索引示意图:
InnoDB要求表必须有主键(MyISAM能够没有),若是没有显式指定,则MySQL系统会自动选择一个能够惟一标识数据记录的列做为主键,若是不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段做为主键,这个字段长度为6个字节,类型为长整形。
在聚簇索引之上建立的索引称之为辅助索引,辅助索引访问数据老是须要二次查找。辅助索引叶子节点存储的再也不是行的物理位置,而是主键值。经过辅助索引首先找到的是主键值,再经过主键值找到数据行的数据页,再经过数据页中的Page Directory(页目录)找到数据行。
Innodb辅助索引的叶子节点并不包含行记录的所有数据,叶子节点除了包含键值外,还包含了相应行数据的聚簇索引键。辅助索引的存在不影响数据在聚簇索引中的组织,因此一张表能够有多个辅助索引。在innodb中有时也称辅助索引为二级索引。
Innodb中辅助索引示意图:
经过对比咱们就能够知道为何咱们对主键会有要求:
一、为何不建议使用过长的字段做为主键,由于全部辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。
二、为何用非单调的字段做为主键在InnoDB中不是个好主意,由于InnoDB数据文件自己是一颗B+Tree,非单调的主键会形成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段做为主键则是一个很好的选择。
固然,若是咱们经过索引优化,将辅助索引优化成覆盖索引,那么辅助索引也包含全部须要查询的字段的值。也就是咱们的索引就是咱们要的值,无需再访问主索引了。这里,涉及到索引的优化不过多介绍!
上面我介绍了在InnoDB存储引擎下的聚簇索引与辅助索引的实现,由于若是没有说明具体的数据库和存储引擎,默认指的是MySQL中的InnoDB存储引擎。可是咱们MySQL还支持MyISAM存储引擎,它也是支持聚簇索引与辅助索引的。
可是该引擎下的实现却有些不一样,咱们的聚簇索引和辅助索引没有什么区别,他们的叶子节点都不存放数据,而是存放数据记录的地址。惟一的区别就是聚簇索引要求key是惟一的,而辅助索引的key能够重复。
因此若是严格的按照聚簇索引叶子节点存放数据来定义的话,MyISAM的索引都只能算是非聚簇索引!聚簇索引,或者严格说主键索引示意图:
辅助索引示意图:
为了更形象说明这两种存储引擎下两种索引的区别,咱们假想一个表以下图存储了4行数据。其中Id做为主索引,Name做为辅助索引。图示清晰的显示了聚簇索引和非聚簇索引的差别。
咱们经过结构对比了使用索引的好处,总结下来的话就是:
可是咱们要知道,索引并非最好的解决方案。总的来讲,只有当索引帮助存储引擎快速找到记录带来的好处大于其带来的额外工做时,索引才是有效的。
这里仅仅是介绍了索引结构原理等,关于索引还有不少,如全文索引,空间索引等,以及索引的优化之类,这更可能是咱们要去学习的。
高性能MySQL(第三版)