索引是数据库中一个很重要的概念,那么什么是索引呢,通俗的讲,索引是存储引擎用于快速找到记录的一种数据结构,就如同书的目录,当要查找某一行记录时,能够在索引中快速定位所在的位置信息,而后就可直接获取目标行的记录。mysql
既然索引的出现是为了提升查找效率,那么确定会存在不一样的索引结构(模型),不一样的索引模型确定有其适应的场景,在下面文章中,咱们将重点讲解常见的索引模型及其特色。算法
用于提升读写效率的数据结构有不少,咱们常见的有哈希表、数组和搜索树。sql
哈希表是以键值对(key-value)存储的数据结构,根据key查找value,只有Memory引擎支持哈希索引,它根据哈希函数将列值key转换成实际物理位置,而后将value存放在该数组中。 数据库
可是,多个key通过能够计算后可能会出现同一个位置的状况,这种状况被称为哈希冲突,咱们能够用链地址法来解决,其思路是在有冲突的数组索引位置拉出一个链表 编程
哈希索引使用的是散列算法,因此存储的位置很分散,所以该索引适用于等值查询的场景中,不适用于范围查询。数组
数组索引的思路很简单,它能够根据索引的位置快速找到存储的元素,又由于数组是有序的,因此该索引在范围查询中效果较好。 缓存
若是仅仅是查询,采用有序数组的索引确定是很是合适的,可是当向数组中插入数据时,就须要在插入的位置后的元素都向后移,效率很低,所以有序数组索引仅仅适用于查询操做。性能优化
树也是一种数据结构,它综合数组和链表的特色,在保证检索速度的基础上,同时也保证了数据的插入、删除和修改的速度。markdown
咱们以二叉搜索树为例,咱们看一下二叉搜索树结构 数据结构
二叉所搜树定义了在非叶子结点中,左子结点的值小于结点值,右子结点的值大于等于结点值,这样的二叉树称为二叉排序树。加入咱们要查找2,它走的路径为7-->3-->1-->2,查询的时间复杂度为O(log(N)),可是这个时间复杂度依赖于这棵树为平衡二叉树。
树是能够存在多叉的,即多叉树中的每一个结点存在多个子结点,子结点的数据大小要保障从左到右是递增的。二叉树是搜索效率最高的,可是在数据库中并不适用二叉树,由于索引不只存在内存中,还要写到磁盘中,为了让一个查询尽量减小读磁盘次数,就必须让查询访问尽可能少的数据块,所以使用最多的是多叉树。
经过上面的内容,了解了多叉树因为读写性能的优势,以及适配磁盘的访问模式,已经被普遍应用在数据库引擎中了。实际上,InnoDB使用的树模型是B+树,数据库表是根据主键的顺序以索引的形式存放的,每个索引都对应着一个B+树,在学习B+树以前,咱们先了解下B树,最后对比说明为何数据库索引选择B+树而不选择B树。
B树是一种多叉自平衡搜索树,它与普通二叉树的区别在于容许每一个结点有更多的子结点。它设计思想是将更多相关的数据尽可能集中在一块儿,以便一次读取多个数据,减小磁盘操做的次数,它可以最大化的优化大块数据的读和写操做,加快了了存取速度。
B树有如下特色:
- 关键字分布在整个整棵树中
- 任何一个关键字只会出如今树中的某一个节点
- 搜索效率等价于二分查找
它的优势是能够在内部结点同时存储键和值,所以,将频繁访问的数据存放在靠近根结点的地方将会提升热点数据的查询效率,这种特性使得B树在特定数据重复屡次查询的场景中很是的高校。
B+树是对B树的进一步优化,B+树图以下
B+树的特色
在InnoDB中使用的索引是B+树,那么为何B+树比B树更适合作数据库的索引呢
B+树中的内部结点没有存放数据,因此其内部结点与B树相比较也就越小,若是把全部同一内部节点的关键字都存放在同一盘块中,该盘块容纳的关键字数量就会越多,查询数据时读入的内存的关键字机会越多,读写IO的次数就会下降
B+树的非叶子结点并非指向文件内容的结点,仅仅是叶子结点中的关键字索引。因此任何关键字的查找必须走一条从根结点到叶子结点的路。全部关键字查询的路径长度相同,致使每个数据的查询效率至关。
B树在提升了IO性能的同时并无解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只须要去遍历叶子节点就能够实现整棵树的遍历。并且在数据库中基于范围的查询是很是频繁的,而B树不支持这样的操做或者说效率过低。
在B+树中,根据叶子结点的内容,能够将索引类型分为主键索引和非主键索引
主键索引的叶子结点存放的是所查字段的整行数据,在InnoDB里,主键索引也被称为聚簇索引,它的索引和数据是存入同一个.idb文件中的,所以它的索引结构是在同一个树节点中同时存放索引和数据
非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引。
咱们了解了主键索引和普通索引以后,那么它们之间有什么区别呢?
- 若是语句是 select * from T where ID=XXX,即主键查询方式,则只须要搜索 ID 这棵 B+ 树;
- 若是语句是 select * from T where k=XXX,即普通索引查询方式,则须要先搜索 k 索引树,获得 ID 值,再到 ID 索引树搜索一次。这个过程称为回表。
也就是说,基于非主键索引的查询须要多扫描一棵索引树。所以,咱们在应用中应该尽可能使用主键查询。
那么有没有可能存在这样一种状况,仅仅使用普通索引而不须要回表就能够拿到所需的数据呢?答案是能够的,覆盖索引就能够知足这样的要求。
覆盖索引就是select的数据列只用从索引中就可以取得,没必要从数据表中读取,换句话说查询列要被所使用的索引覆盖。即普通索引中除了包含指向的ID以外,也能够存放数据。
由于覆盖索引能够减小树的搜索次数,显著提高查询性能,全部覆盖索引是一个经常使用的性能优化手段。
联合索引又可称为复合索引,对于这种类型的索引,MySQL从左到右的使用索引中的字段,一次查询只能使用索引中的一部分,而且是作左侧部分,知足最左前缀原则。
最左前缀原则能够这样理解,例如索引key index(a,b,c),它就支持索引(a),(a,b),(a,b,c)3种类型的组合进行查找,不支持索引b、c查找。
利用符合索引中的附加列,能够缩小搜索的范围,但使用一个具备两列的索引 不一样于使用两个单独的索引。复合索引的结构与电话簿相似,人名由姓和名构成,电话簿首先按姓氏对进行排序,而后按名字对有相同姓氏的人进行排序。若是您知道姓,电话簿将很是有用;若是您知道姓和名,电话簿则更为有用,但若是您只知道名不姓,电话簿将没有用处。
惟一索引是索引列中的元素是不可重复的,只能出现一次,它与普通索引有以下的区别
查询操做
普通索引:查找到知足条件的第一个记录后,须要查找下一个记录,直到碰到第一个不知足条件的记录
惟一索引:因为索引定义了惟一性,查找到第一个知足条件的记录后,就会中止继续检索
所以,普通索引相对于惟一索引要多一些操做。可是,他们之间对于性能的差距倒是微乎其微
更新操做
普通索引:当须要更新一个数据页时,若是数据页在内存中就直接更新,而若是这个数据页尚未在内存中的话,在不影响数据一致性的前提下,InnoDB会将这些更新操做缓存在change buffer中,这样就不须要从磁盘中读入这个数据页了。在下次查询须要访问这个数据页的时候,将数据页读入内存,而后执行change buffer中与这个页有关的操做。经过这种方式就能保证这个数据逻辑的正确性。
惟一索引:全部的更新操做都要先判断这个操做是否违反惟一性约束,而这必需要将数据页读入内存才能判断。若是都已经读入到内存了,那直接更新内存会更快,就不必使用change buffer了。
总结
在查询操做中,由于俩者区别不大,选择任何一种都是能够的。在更新操做中,惟一索引相较于普通索引更加消耗IO资源,因此选择普通索引。综合分析来看,选择普通索引。
简单来讲,索引下推是数据库检索数据过程当中为减小回表次数而作的优化。
案例
有一个联合索引(name,age),如今的需求是检索出表中的名字的第一个字是郎,并且年龄是23岁的男生,SQL能够这样写
mysql> select * from tuser where name like '郎%' and age=10 and ismale=1; 复制代码
MySQL5.6以前,
无索引下推
在最左前缀前提下,只能利用最左边的索引“郎”,找到第一个知足条件的ID,而后进行回表,到主键索引上找出数据行,再对比字段值。
有索引下推
有索引下推能够在索引遍历过程当中,对索引中包含的字段先作判断,直接过滤掉不知足条件的记录,减小回表次数。即InnoDB 在 (name,age) 索引内部就判断了 age 是否等于 10,对于不等于 10 的记录,直接判断并跳过。在咱们的这个例子中,只须要对 ID四、ID5 这两条记录回表取数据判断,就只须要回表 2 次。
参考文章
[1]林晓斌.《MySQL实战45讲》
[2]https://www.jianshu.com/p/0371c9569736
关注公众号:10分钟编程
公众回复success领取学习资源