深入理解MySql索引底层数据结构

148 阅读8分钟

什么是索引

索引是一种排好序的数据结构,用于帮助我们在大量数据中快速查找到我们想要查找的数据。

常见索引数据结构

二叉查找树、红黑树(弱平衡二叉树)、B树、B+树

各数据结构的特点

二叉查找树:

二叉查找树是指一棵空树或者具有下列性质的二叉树:

  • 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值
  • 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值
  • 任意节点的左、右子树也分别为二叉查找树
  • 没有键值相等的节点 BinarySearchTree_1.png 上图是一个普通二叉查找树的搜索11的过程,先找到根,其键值是8,8小于11,因此查找8的右子树,找到10;而10小于11,再找其右子树;一共找了3次。如果是顺序查找(4、6、7、8、9、10、11)则查找11需要6次,可见二叉查找树比起顺序查找要快。

但是不排除某些例外,在某些情况下二叉查找树会退化为线性表从而变成线性查找,如下图: 如果二叉树的根节点是最小或者最大的数,那么二叉查找树就完全退化成了线性结构,此时它的查找效率就会很低,查找5需要5次,跟顺序查找差不多。

BinarySearchTree_2.png

平衡二叉树AVL

平衡二叉树是为了解决二叉查找树退化为线性表的情况而诞生的,平衡二叉树具有如下特点:

  • 具有二叉查找树的全部特性。
  • 每个节点的左子树和右子树的高度差至多等于1

如下图:左边的就是一颗平衡树,右边则不是

AVL_Tree_1.png

平衡二叉查找树,一般是用平衡因子差值判断是否平衡并通过旋转来实现平衡,必须满足所有节点的左右子树高度差不超过1,不管执行插入还是删除操作,只要不满足这个条件,就需要通过旋转来保持平衡。这种通过旋转来维护高度平衡是非常耗时的。

红黑树

红黑树是为了解决平衡树在插入、删除等操作需要频繁调整的情况而诞生的。红黑树具有以下特性:

  • 每个节点要么是黑色,要么是红色。
  • 根节点是黑色。
  • 每个叶子节点(NIL,叶子节点不存数据)是黑色。
  • 每个红色结点的两个子结点一定都是黑色。
  • 任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

红黑树是一种弱平衡二叉树(由于是弱平衡,可以推出,相同的节点情况下,AVL树的高度低于红黑树),相对于要求严格的AVL树来说,它的旋转次数变少。

关于红黑树的更多详细内容,大家可以参考以下链接: www.jianshu.com/p/e136ec792…

小结:

二叉查找树局限:某些情况下退化成线性表,导致树变得不平衡,树的高度太高,从而导致查找效率不稳定。

平衡二叉树和红黑树:为了解决退化成线性表的情况,加上保证树的结构是平衡的条件,每当插入或删除数据时都有可能导致树不满足平衡条件,从而会调整树上的节点来保持平衡,相比二叉查找树来说,查找效率更稳定,但是当数据量很大的时候,树的高度依旧很高。

用树这种数据结构作为索引的数据结构,那么每查找一次数据,就需要从磁盘中读取一个节点(即磁盘块),以上二叉查找树、平衡二叉树、红黑树都是每个节点只存储一个键值和数据的。如果用其作为索引,当存储海量数据的时候,树的节点会非常多,树的高度也会很高,查找数据时就会进行很多次的磁盘IO(和内存相比,从磁盘中读取数据的速度会慢上百倍千倍甚至万倍),这样查找数据的效率也会变得极其低。

B树

B树:是一种单个节点就可以存储多个键值和数据的平衡树,它是为了解决以上二叉树的弊端而诞生的。B树相对于二叉树,每个节点存储了更多的键值和数据,并且每个节点拥有更多的子节点,这样存储海量数据时,树的高度会比二叉树低。基于这些特性,B树查找数据读取磁盘的次数就会相应的减少很多。数据的查找效率自然也会比二叉树高很多。

BTree_1.png

B+树

B+树:是对B树的进一步优化。具有以下特点:

  • B+树的非叶子节点上是不存储数据的,仅存储键值。之所以这么做是因为数据库中页的大小是固定的,Mysql innodb中页的默认大小是16KB,如果不存储数据,那么就可以存储更多的键值,相应的树的阶数(节点的子节点数)就会更大,树的高度就会更矮,这样查找数据进行磁盘IO的读取次数就会更加减少,树的查询效率也会更快。例如:MySQL中页的默认大小是16KB,一个索引占8个字节,一个索引的子节点占6个字节,假设树的深度为3,一行数据为1KB,那就可以存储2千多万条的数据。
  • B+树的阶数是等于键值的数量的,假设B+树的一个节点可以存储1000个键,那么3层B+树就可以存储100010001000=10亿个数据。一般根节点是常驻与内存中的,所以查找10亿数据的时候,实际只需要2次的磁盘IO.
  • B+树的所有数据都存储在叶子节点上,而且数据是按照顺序排列的。因此B+树使得范围查找、排序查找、分组查找、以及去重查找变得更加简单。而B树的数据是分布在各个节点中的,要实现这点很不容易。
  • 另外,在MySQL的innodb引擎中,给B+树的各个叶子节点都加上了双向指针,提高了区间访问的性能。
  • B+树的高度取决于非叶子节点能够存放多少个索引元素。

B+Tree_1.png

MyISam 存储引擎的索引实现(非聚簇索引):

  • 索引文件(存储在MYD文件中)和数据文件(存在MYI文件中)是分离的。
  • 非聚簇索引-叶子节点的数据放的是索引所在行的磁盘文件地址
    MyISAM_Index.png

InnoDB存储引擎的索引实现(聚集索引):

  • 表数据文件本身就是按B+树组织的一个索引结构文件(idb文件)
  • 聚集索引-叶结点包含了完整的数据记录

InnoDB_Index.png

为什么建议InnoDB表必须建主键,并且推荐使用整形的自增主键?

  • MySQL 开发人员在设计 InnoDB表的时候,ibd文件在存储数据时必须用一颗B+树来组织,如果表中有主键,就可以用自带主键自带索引来组织整张表的数据。如果不建主键,MySQL会从表中第一列开始选取一列所有元素都不相等的列作为索引数据来组织idb B+树数据文件。如果选不到,MySQL 会帮忙建一个隐藏列,这个列的所有元素都是唯一的。由于MySQL的资源是很宝贵的,所以开发人员给表建一个主键的话,就可以省去MySQL自动帮我们维护数据表的资源成本。

  • 使用整形的主键在比大小的速度上相对于用uuid主键会快很多。另外一个索引占用空间越小,整个索引占用的空间就越小,对硬盘的存储空间也占用越小,节约硬盘资源。

  • 使用自增的主键,是为了减少树的平衡操作次数,提高数据插入效率。

聚集索引跟非聚集索引在查找速度上,聚集索引会快一点。

非主键索引

MyISam 非主键索引跟主键索引的存储结构是一致的。但InnoDB的主键索引和非主键索引存储结构是不一致的。如下图: InnoDB非主键索引的叶子节点存放的是聚集索引的索引值(InnoDB每张表只有一个聚集索引/主键索引)。这样子的结构主要是可以节省空间,同时也方便维护表数据的一致性。

SecondaryKey.png

联合索引底层存储结构

联合索引也就是复合索引,也就是由多个字段共同组织成一个索引。如下图所示:

Lianhe_index.png

最左前缀原则

对于联合索引,MySQL从左到右的使用索引中字段,一个查询可以只使用索引中的一部分,但只能是最左侧部分。例如索引是key_index(a,b,c),可以支持使用 a | a,b | a,b,c 3种组合进行查找。但不支持b | b,c进行查找。

创建联合索引时需要考虑列的顺序,对索引中的所有列执行搜索或仅对前几列执行搜索时,联合索引非常有用;仅对后面的任意列进行搜索时,复合索引则没有用处。