Mysql 索引笔记

1,072 阅读6分钟

跟索引相关的算法

二分查找发

二分查找法的查找过程是:将记录按顺序排列,查找时先以有序列的中点位置为比较对象,如果要找的元素值小于该中点元素,则将查询范围缩小为左半部分;如果要找的元素值大于该中点元素,则将查询范围缩小为右半部分。以此类推,直到查到需要的值。

二叉查找树

二叉查找树中,左子树的键值总是小于根的键值,右子树的键值总是大于根的键值,并且每个节点最多只有两颗子树。

平衡二叉树

我们一起看下平衡二叉树的定义:满足二叉查找树的定义,另外必须满足任何节点的两个子树的高度差最大为 1。

缺点是每个节点最多只有两个分支,如果数据量比较大,要经历多层节点才能查询在叶子结点的数据。

B 树

B 树可以理解为一个节点可以拥有多于 2 个子节点的多叉查找树。B 树中同一键值不会出现多次,要么在叶子节点,要么在内节点上。

与平衡二叉树相比,B 树利用多个分支(平衡二叉树只有两个分支)节点,减少获取记录时所经历的节点数。

缺点是:因为每个节点都包含 key 值和 data 值,因此如果 data 比较大时,每一页存储的 key 会比较少;当数据比较多时,同样会有:“要经历多层节点才能查询在叶子节点的数据” 的问题。

B+ 树

B+ 树是 B 树的变体,定义基本与 B 树一致,与 B 树的不同点:

  • 所有叶子节点中包含了全部关键字的信息
  • 各叶子节点用指针进行连接
  • 非叶子节点上只存储 key 的信息,这样相对 B 树,可以增加每一页中存储 key 的数量。
  • B 树是纵向扩展,最终变成一个 “瘦高个”,而 B+ 树是横向扩展的,最终会变成一个 “矮胖子”(这里参考了《MySQL 运维内参》第 8 节 B+ 树及 B 树的区别中的比喻)。

在 B+ 树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点上。B+ 树中的 B 不是代表二叉 (binary) 而是代表(balance),B+ 树并不是一个二叉树。

与 B 树最大的区别是:它的键一定会出现在叶子节点上,同时也有可能在非叶子节点中重复出现。而 B 树中同一键值不会出现多次。

B + 树索引

在数据库中,B+ 树的高度一般都在 2 ~ 4 层,所以查找某一行数据最多只需要 2 到 4 次 IO。而没索引的情况,需要逐行扫描,明显效率低很多,这也就是为什么添加索引能提高查询速度。

B+ 树索引并不能找到一个给定键值的具体行,B+ 树索引能找到的只是被查找数据行所在的页。然后数据库通过把页读入到缓冲池(buffer pool)中,在内存中通过二分查找法进行查找,得到需要的数据。

InnoDB 中 B+ 树索引分为聚集索引和辅助索引。

聚集索引

InnoDB 的数据是按照主键顺序存放的,而聚集索引就是按照每张表的主键构造一颗 B+ 树,它的叶子节点存放的是整行数据。

InnoDB 的主键一定是聚集索引。如果没有定义主键,聚集索引可能是第一个不允许为 null 的唯一索引,也有可能是 row id。

由于实际的数据页只能按照一颗 B+ 树进行排序,因此每张表只能有一个聚集索引(TokuDB 引擎除外)。查询优化器倾向于采用聚集索引,因为聚集索引能够在 B+ 树索引的叶子节点上直接找到数据。

聚集索引对于主键的排序查找和范围查找速度非常快。

关键信息:

  • 根据主键值创建了 B+ 树结构
  • 每个叶子节点包含了整行数据

辅助索引

InnoDB 存储引擎辅助索引的叶子节点并不会放整行数据,而存放的是键值和主键 ID。

当通过辅助索引来寻找数据时,InnoDB 存储引擎会遍历辅助索引树查找到对应记录的主键,然后通过主键索引来找到对应的行数据。

关键信息:

  • 根据普通索引的值创建了 B+ 树结构
  • 每个叶子节点保存的是普通索引自己的键值和主键 ID

需要添加索引的场景

数据检索

数据检索时,在条件字段添加索引。

聚合函数

索引能提升 max() 函数的效率,同理也能提升 min() 函数的效率,之前也有分析过索引对 count(*) 也有优化作用。

排序

  • 如果对单个字段排序,则可以在这个排序字段上添加索引来优化排序语句;
  • 如果是多个字段排序,可以在多个排序字段上添加联合索引来优化排序语句;
  • 如果是先等值查询再排序,可以通过在条件字段和排序字段添加联合索引来优化排序语句。

避免回表

select a,d from t9_1 where a=90000;
/*a 字段添加了索引,d 字段没有添加索引*/

因为辅助索引的结构,通过辅助索引寻找到对应记录的主键,然后通过主键索引回表去找对应的行数据。

如果条件字段和需要查询的字段有联合索引的话,其实回表这一步就省了,因为联合索引中包含了这两个字段的值。像这种索引就已经覆盖了我们的查询需求的场景,我们称为:覆盖索引。

关联查询

通过在关联字段添加索引,让 BNL变成 NLJ 或者 BKA。

联合索引

是指对表上的多个列进行索引。适合 where 条件中的多列组合,在某些场景可以避免回表。联合索引如图所示。

对于 a、b 两个字段都做为条件时,查询是可以走索引的;对于单独 a 字段查询也是可以走索引的。但是对于 b 字段单独查询就走不了索引了,因为在上图,b 字段对应的值为 1,2,3,1,2,3,显然不是有序的,所以走不了 b 字段的索引。

联合索引的建议:

  • where 条件中,经常同时出现的列放在联合索引中。
  • 把选择性最大的列放在联合索引的最左边。