直接起飞
1.什么是索引?
索引是帮助mysql高效获取数据的排好序的数据结构。
2.索引的数据结构?为什么选这种结构?
假设我们现在这里有一张表(以下情况都是innodb存储引擎):
id | number |
1 | 33 |
2 | 18 |
3 | 14 |
4 | 22 |
5 | 57 |
6 | 8 |
7 | 20 |
8 | 72 |
9 | 51 |
如果mysql没有索引这种结构,那么我们如果查找number为51的这行记录,那么mysql就要从上往下扫描全表,找到number为51的这行记录需要9次;
二叉树?
如果mysql索引结构为二叉树呢?
我们要去查找51这个元素:
我们只需要查找3次就能找到51这个元素,时间复杂度为O(3);
那么我们mysql索引用的就是二叉树结构吗?
肯定不是的,如果是二叉树,那么我把id这一列作为索引会是怎么样的情况:
假设我们现在要查找id为7的这一列:
这个时候二叉树已经是链表化了,我们查找7这个元素就需要找7次,时间复杂度为O(7);
二叉树对于连续自增的数据会转为链表化,不适合做这种数据的查找。我们排除这种数据结构。
红黑树?
假设mysql索引为红黑树,这个时候我们还是先去查找number为51的这一行:
我们需要查找4次就能找到51这个元素,时间复杂度为O(4);
再看如果查找id为7的元素呢?
这次找id为7的元素,只找了4次,比二叉树次数少多了;
那么mysql用的是红黑树吗?我们来思考一个问题:
如果我数据库里面有几千万条数据,也就是说,id是自增的几千万个,那么这个红黑树高度是不是很高,查询元素时间复杂度为O(n);这显然是不符合我们mysql高数据量的时候查找;
hash表?
查找id为7,只需要一次,很快,很棒,这也是mysql索引的一种;
但是,我们来想想这有什么缺陷:
1.hash冲突问题;
2.如果我想查找2<id<6的这些元素呢?hash还能找的到吗?
答案肯定是不行的,hash只适合做简单查询,但是它效率非常高,只需要去hash表中查找一次就能精确定位到数据,但是hash表只能做一次hash运算去查找元素,像前面的情况,就无法使用索引去查找元素了。
来验证一下,我们给test表加上hash索引:
查询一个money的区间:
直接走的全表扫描,并没有走索引;
B-Tree?
我们还是来查找money为51的这行数据:
我们可以看到,一颗高度为3的b tree上,我们去查找元素时间复杂度最多为O(3);我们去查找51也是查找3次,很好的解决了元素查找问题;
再看这棵树上面,叶子节点都是排好序的,但是需要去查找的话,还是需要从根节点开始查找,并没有完美解决hash表不能做的范围查找;(范围查找时间复杂度高)
那么mysql用的是b tree吗?mysql用的不是b tree,我们下面来看b+tree;
B+Tree
mysql索引使用的就是b+tree;为什么mysql会选用b+tree呢?我们来看b+tree的结构:
从结构上来看,b+tree多了冗余数据,叶子节点上面有指向下一个叶子节点的指针;
如果是范围查找,我查找到一个数据,那么根据叶子节点指针就能顺藤摸瓜找到这个范围能的数据,完美解决了hash表的范围查找;
b+tree跟b tree还有一个区别点是b+tree(数据库这行data)只放在了叶子节点;而b tree在第二行数据块上面也存有着 (数据库这行data),一个数据库只有16kb大小,容量有限,在有限容量下,b+tree的冗余设计要比b tree能存放更多的数据;
3.为什么选用InnoDb存储引擎
1.表数据文件本身就是按照b+tree组织的文件存储,主键索引上面包含了整行的全部数据,节省了磁盘空间;
2.聚集索引(主键索引)的叶子节点包含了整行数据;MyISam引擎文件和数据是非聚集的(索引文件myi和数据文件myd并不是在一个文件,这一点innodb都是存放在ibd文件);
3.innodb支持事务;
4.各种索引
4.1主键索引(聚集索引)
以主键数据作为索引列,叶子节点上包含了行所有的数据:
4.2稀疏索引(辅助索引,非聚集索引)
以非主键列作为索引,叶子节点上面只有id,如果需要查询这行全部数据,需要去主键索引上面回表:
4.3覆盖索引
所谓覆盖索引,其实上就是非主键索引,但是被称做覆盖索引是因为sql的查询项中只包含了索引列和id,不需要去主键索引回表,这样的操作就称为覆盖索引查询。
4.4联合索引
所谓的联合索引,其实就是由多个字段组成的辅助索引;符合最左前缀匹配规则(结合索引的有序性,就能明白为什么是最左前缀匹配原则;后面sql优化中会讲到这些):
问题1:为什么非主键索引叶子节点存储的是主键?
可以节省磁盘存储空间,ibd文件会相对较小;
行数据做更改的时候并不影响辅助索引,只存储id的奥妙所在(更改不需要去维护索引);
问题2:为什么innodb存储引擎的表必须要建主键?并且推荐使用自增主键?
如果没有主键就无法维护ibd文件,innodb必须要有索引;如果建表的时候没有设置主键,innodb会维护一个隐藏列去作为主键索引;
自增索引插入的时候效率要比其他要高出很多;而且作为int或者long,比起uuid所占用字节是要少的多的,也就是说,上层荣誉节点可以放更多,下层叶子节点也可以放更多的数据。