MySQL 索引与 B+ 树
MySQL 索引与 B+ 树
B+ 树
MySQL Innodb 存储引擎是使用 B+ 树来组织索引的。在介绍 B+ 树以前,先认识一下什么是 B 树,B 树是平衡二叉树,与一般的二叉查找树不同,平衡二叉树首先满足二叉查找树的定义(左子树的键小于根的键,右子树的键大于根的键),其次必须满足任何节点的两个子树的高度最大差为 1。B 树的维护要求插入和更新节点时,通过 1 次或多次左旋和右旋来满足平衡的条件。二叉查找树是否平衡直接影响了查找需要比较的次数。
B+ 树与普通的二叉树不同,它的节点由多个关键字和指向子树的指针组成,指向子树的指针个数等于关键字个数加 1,这些子树中关键字的范围由它的父节点限定,真正的数据部分是存放在叶子节点中的。MySQL 中页数据结构就是这些叶子节点,每个叶子节点对应了一个 Page,而 Page 的数据结构中有 PAGE_PREV 和 PAGE_NEXT 两个指针,因此这些叶子节点两两之间也是相互连接的。
B+ 树的插入操作
由于 B+ 树需要在插入后依然保证平衡,因此插入操作会涉及到页的拆分操作。Index Page 指的是非叶子结点而 Leaf Page 指的是叶子节点。插入操作分为以下三种情况:
- 当 Index Page 和 Leaf Page 都不满时,直接将记录插入到叶子节点中。
- 当 Index Page 不满,Leaf Page 满时,先将节点放入对应 Page,以中间节点作为依据,将 Page 拆分,然后将中间节点放入 Index Page 中,拆分后的左右记录分别放在中间节点的左右两边。
- 当 Index Page 和 Leaf Page 都满了,先拆分 Leaf Page,然后再拆分 Index Page,拆分 Index Page 的方法与拆分 Leaf Page 的方法一样。
此处需要注意:为了在可能地情况下减少页的拆分操作,B+ 树提供了类似二叉平衡树的旋转操作。旋转操作发生在 Leaf Page 已经满,但是其左右兄弟节点没有满的情况下。
B+ 树的删除操作
B+ 树使用填充因子来控制树的变化,即中间节点关键字的数量和叶子节点关键字的数量和最大值的比例关系。此处以填充因子为 50% 为例。小于填充因子即为小于总容量的一半。删除操作可以分为以下三种情况:
- 当 Leaf Page 关键字个数和 Index Page 节点关键字的个数都不小于填充因子时,直接将记录从 Leaf Node 中删除,如果该节点为 Index Page 节点,那么将 Index Page 节点替换为其右节点。
- 当 Leaf Page 关键字个数小于而 Index Page 不小于填充因子时,将 Leaf Page 节点和其兄弟节点合并,同时更新 Index Page 节点。
- 当 Leaf Page 关键字个数和 Index Page 节点关键字的个数都小于填充因子时,在情况 2 的基础上还需要合并 Index Page 节点。
索引
索引在 MySQL 中就是使用 B+ 树实现的,不同索引之间形成的 B+ 树也是不同的。
聚集索引
聚集索引就是根据主键来构造 B+ 树,叶子节点存放对应页的行记录。
辅助索引(非聚集索引)
辅助索引就是使用非主键构造的 B+ 树,叶子节点存放的是对应的键值以及相应的聚集索引键。通过辅助索引来搜索一般是两级的,第一级找到键值对应的聚集索引键,第二级是根据聚集索引键寻找行记录。
联合索引
联合索引就是对表上的多个列进行索引,这样构造的 B+ 树的 Index Node 和 Page Node 包含多个键。
索引覆盖
在联合索引的情况去搜索行记录,假设需要的行记录的列正好包含在联合索引中,那么此时结果将可以直接从联合索引中得到,省去了从聚集索引中搜索,由于不包含整行的记录所以可以大大减少 IO。
不使用索引的情况
当查询满足条件的行的所有列时,MySQL 不使用辅助索引,而是直接使用聚集索引。原因是即使使用了辅助索引,还是必须通过叶子节点中的目录进行聚集索引的查找,才能得到完整的信息,那么直接从聚集索引中获取即可。