一、什么是索引
一句话概述:索引可以让MySQL快速的获取数据并且是一种排好序的数据结构。
二、索引底层数据结构的选择
在计算中常见的数据结构有很多,我们从如下的几个数据结构中分析一下MySQL是如何选择的
- 链表:链表是一种非常常见的数据结构,获取数据的时间复杂度是O(n),在链表中插入一个数据时间复杂度是是O(1)。但是在MySQL中为何没有选择链表作为底层的数据结构呢?我们想想平时在一张表中一般都是存在几百万甚至上千万的数据。就单纯使用一个字段做索引链表的长度都是几百万甚至上千万个节点,这么大的数据量在查询的时候可能比全表扫描慢。
- 二叉树:既然链表是由于查询时间复杂度太高不适合做索引,那二叉树呢?二叉树的时间复杂是log2n在查询的时间复杂度上的确是降低了不少。但是也架不住当二叉树退化为链表的情况如下图所示:
好像时间复杂度又退回到O(n) - 红黑树:既然二叉树在极端情况下回退化为链表,使用红黑树会自动平衡左右子树之前的高度差,还是上面的数据在平衡二叉树中的存储结构如下:
红黑树看起来似乎没有太大的问题,假如索引元素有一千万个那树的高度是多少呢?
(2^h-1) = 10000000 => h大约在23到24之间,也就是说第24层没有放满就会到一千万个数据
最坏的情况下要遍历24个节点才会才会查询到数据,相对于而二叉树在极端情况下来说已经非常优秀了。那还有没有更近一步的优化方案呢?
4. B树:假如使用红黑树作为索引底层的数据结构已经非常不错了,那考虑是否可以在同一个节点存储多个元素呢?而且这些元素都是有序的。实际上就是B树。其存储结构如下:
从上面的存储结构可以看出在一个节点上使用二分查找,定位到数据的范围而后继续向下查询,这样查询的次数很很大程度的降低了下来。这个方案比之前的红黑树又更近一步的优化。但是在日常开发中我们会涉及到范围查询,各种排序查询这样的数据结构查询起来不是那么友好。
5. B+树:基于上述的查询需求我们可以想到一种数据结构那就是B+树,其叶子节点是使用指针相互连接的而且在叶子节点存储了所有元素。存储上述的数据数据结果如下所示:
但是这样的数据结构没有办法实现从后向前的数据遍历,在MySQL中使用的也不是B+树而是改良之后的B+树。
6.改良之后的B+树,改良之后的B+树存储数据的存储结果如下:
到此为止mysql底层数据结构使用的就是这样的数据结构
三、主键索引、普通索引、联合索引之间的关系
- 在了解完索引底层数据结构看看主键索引在索引树上的存储结构:
- 普通索引的数据存储结构
从上述的数据结果可以在非主键索引的叶子节点存储的是主键Id,所以在这里就会涉及到一个概念就是回表,简单的说就是在非主键索引中查询到Id然后在根据id去主键索引树中去获取具体的数据 - 联合索引数据存储结构
所以在创建索引时并不是索引越多越好,当存在多个普通索引树查询时会出现多次的回表操作,能适应一个联合索引搞定的查询尽量使用联合索引,因为联合索引不但能减少回表的次数还能根据索引下推做更多的优化。
四、MySQL中索引分类以及常见索引分类
- 在MySQL中常见的索引类型由如下的几种
BTREE
Hash
Hash适合精确查询,但是不适合范围查询
2. 聚集索引、聚簇索引、稀疏索引
聚集索引、聚簇索引:数据和索引是放在同一个文件中,InnoDB引擎就是使用聚集索引
稀疏索引:索引和数据是存储在不同的文件中,首先在索引文件中读取到索引,然后在去数据文件中读取数据
四、索引的最左匹配原则以及索引下推
- 最左匹配原则
还是上面的联合索引:idx_name_age
当执行如下的查询的时候是走索引的:
select * from user where name = '小张' and gae = 18
但是下面的语句不走索引
select * from user where gae = 18
简单总结一下:最左匹配原则简单来说会按照创建索引的顺序去做匹配,中间不能有断层
3. 索引下推:在非主键索引树中叶子节点是存储索引树的Id之前的查询是根据一个条拿到主键Id然后去主键索引树中获取对应的数据然后在做过滤,这样过滤显然不是很高效。之后就引入索引下推的概念,在非主键索引树中将所有的条件全部过滤完成得到主键Id然后在去主键索引树中获取数据。
五、为何在设计表的时候主键推荐是自增的
本人自己的理解是这样的:根据上面的知识了解到索引的底层数据结构是基于改良之后的B+树去做的,假如主键不是自增的,那么在构建的时候会不断的平衡、分裂。而且这样的操作会比使用自增主键的多很多,这样无疑增加了MySQL的负担。