什么是索引
索引是帮助MYSQL高效获取数据的排好序的数据结构。
简单来说索引就是一种特殊的数据结构,且是有序的。
为什么是排好序的?
那又是什么样的数据结构?二叉树?B-Tree?红黑树?
为什么会选择B+Tree?它的结构是什么样子的?和B-Tree有什么区别吗?为什么不选择二叉树?
先来看看二叉树吧
二叉树
二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个结点。 ——来自百度百科
以下图为例:
如果以Col2,生成的二叉树作为索引如上图所示
当我们运行查询语句:select * from where t.col2=89。会去扫描二叉树2次就找到了。如果这张表的数据量很大,十万,百万,这个时候二叉树的深度就很会很深。这时候查找89的数据效率将会变得非常低。
再看另外一种情况:
如果此时使用Col1来构建二叉树作为索引如下图所示:
此时生成的二叉数只有右子树,相当于一个链表。
当我们运行查询语句:select * from where t.col1=6。此时从二叉树中去查找id为6的数据,相当于需要扫描整个二叉树。如果以mysql自增主键来作为索引,数据量巨大的情况下,那就是全表扫描。这样子的效率也是非常的低。
由上可知:二叉树不适合作为mysql的索引数据结构。
接下来看看红黑树长什么样。
红黑树
红黑树是一种特定类型的二叉树,平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树。但对之进行平衡的代价较低, 其平均统计性能要强于AVL。 ——来自百度百科
如下图所示红黑树的结构:
同样以select * from where t.col1=6 为例,查id为6的数据,此时需要扫描红黑树3次。会比二叉树效率高一些。但是缺点和二叉树一样,表数据量巨大时,红黑树高度会很大,查找效率低下。
同理可知:红黑树也不适合作为mysql的索引数据结构。
接下来来看看B-Tree。
B-Tree
B-Tree有几个特点:
叶子节点具有相同的深度,叶子节点的指针为空
所有索引元素不为空
节点中的数据索引从左到右递增
B-Tree的结构如下图所示:
在mysql中的结构如下图所示:
每个索引元素下面都带有具体的那一行数据的地址
B-Tree可以设置树的最大深度,同样再去运行select * from where t.col1=6 查询数据,首先会从跟节点采用二分查找算法定位到索引元素6的位置,就可以快速的找到相关数据。由此可知,B-Tree的效率会比红黑树和二叉树的效率高的多。
但是,Mysql底层采用的并不是B-Tree,或许mysql的设计者,觉得B-Tree还有继续抢救的空间。。。
于是,在B-Tree的基础上有了变种的B+Tree。
又但是Mysql底层的B+Tree结构与普通的B+Tree又有些许的差别。。。
B+Tree
B+Tree作为B-Tree的变种,在数据结构的格式是与B-Tree有很大的相似程度。同样,我们先看看它的数据结构是什么样子的,如图所示:
B+Tree的数据似乎与B-Tree的结构没有什么不同,仔细看会发现,B+Tree的索引元素存在冗余,也就是说根节点的索引元素在叶子节点中一定可以找到,B+Tree是自下而上的构建索引树,而且在叶子节点的指针会指向下一个叶子节点的索引元素的地址。双向指针的优势在于,mysql在使用范围查找时,可以通过指针快速的去定位到指定的索引位置,而不需要,在向上去跟节点查找定位到数据所在的位置,这一点也是选择B+Tree比B-Tree的优势所在。
再来看看B+Tree在mysql中的应用,如图所示:
与B-Tree在Mysql中不同的是:
B+Tree 非叶子节点不存储data数据,只存储索引(冗余),可以存放更多的索引。
叶子节点包含所有索引元素。
叶子节点直接用指针连接,提高区间访问的性能。
在Mysql中一个节点的数据称为页,也就是一页数据。页的大小默认为16k。也就是说mysql每次会读取16k的数据,或者每16k的数据刷入磁盘。
小结:为什么会选择B+Tree?
从二叉树到红黑树在到B-Tree,到最后的B+Tree,发现影响查询效率的最主要的还是树的高度,因此可以排除二叉树和红黑树。以Mysql一页数据默认为16k来对比B-Tree和B+Tree,为什么会选择B+Tree而不是B-Tree,例如,表的数据同样为2000万。B+Tree的非叶子节点不存储数据,如果索引元素占用8Byte,指针地址占用6Byte,16kb/(8+6)Byte得到的值约为1170,也就是非叶子节点的所有元素可以存储1170个,因为叶子节点会存储data,就以叶子节点的索引元素和data的数据大小为1K来说。以高度为3的B+Tree来说可以存储1170x1170x16个索引元素。与B+Tree不同的是,B-Tree的叶子节点都会存储data数据大小为1K来说。叶子节点最多只能存储16个索引元素。16^n=2000万,n远远大于B+Tree的高度3。因此选择B+Tree。
聚集索引和非聚集索引
InnoDB索引实现(聚集索引)
表数据文件本身就是按B+Tree组织的一个索引结构文件,也就是说聚集索引叶子节点包含了完整的数据记录,索引文件和数据存放在同一个文件下,相反非聚集索引叶子节点包含的只是数据所在的地址信息。
MyISAM索引实现(非聚集索引)
聚集索引和非聚集索引相反,MyISAM存储引擎的索引就是索引文件和数据文件是分离的,分开存储的
非主键索引(非聚集索引)
非主键索引,也就是二级索引,依然使用的是B+Tree构建,不同之处在于非主键索引的叶子节点存储的是主键值,而不是存储完整的数据内容。第一,是为了节省存储空间,因为在主键索引结构中叶子节点已经存储的是完整的列的数据内容,非主键索引的叶子节点就没必要重复存储完整的列的数据内容。二来,是为了保证数据的一致性,如果非主键索引的叶子节点存储的也是完整列的数据内容,那么,在更新、修改、删除列的数据时候需要维护两个文件(一个是主键索引文件,一个是非主键索引文件),增加维护成本。
联合索引
索引最左前缀原理
如上图联合索引,使用了name、age、position作为索引,mysql会按照索引键的先后顺序来维护到索引树中,先会比较name,name相等再比较age、age相等最后比较position的大小顺序来维护到索引树中。
举例:
select * from t where name='Bill' and age = 31 会走索引
select * from t where age = 30 and position = 'dev' 不会走索引
select * from t where position = 'manager' 不会走索引
最后
- 为什么建议InnoDB表要建立主键,并且推荐使用整型的自增主键? InnoDB表在构建索引时候,会优先使用主键来构建索引树,如果表没有主键则会选择其他列中unique的索引类型来构建索引树,如果页没有unique的索引列,则mysql会给这张表建立一个隐藏列rowId,来维护一个唯一的id。在构建索引树的过程中需要对主键进行大小的比较,相对于其他类型,整型更加具有优势。但是为什么要使用自增主键呢?还记得开头所说的索引是排好序的数据结构,也就是说,如果不是使用自增的话,mysql会在构建索引的时候进行一次排序,反之使用自增主键,在构建索引树的时候不需要排序只需要往后面增加就可以了。明显可以看出来自增主键的优势所在。