MySQL InnoDB索引底层原理详解

MySQL的索引类型

从数据结构角度

  1. B+树索引(O(nlogn))
  2. hash索引:
  • 仅仅能够满足”=”,”IN”和”<=>”查询,不能使用范围查询, 无法通过操作索引来排序
  • 检索效率最高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引
  • 不能避免全表扫描,当存在大量相同hash值得时候,hash索引的效率会变低
  1. FULLTEXT索引:支持full-text的字段只有char、varchar、text数据类型, 主要是用来代替like “%*%”效率低下的问题

从物理存储角度

  1. 聚簇索引(clustered index)
  2. 非聚簇索引(non-clustered index)

从逻辑角度

  1. 主键索引(PRIMARY):主键索引是一种特殊的唯一索引,不允许有空值
  2. 普通索引(NORMAL)或单列索引
  3. 多列索引(复合索引):复合索引指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用复合索引时遵循最左前缀集合
  4. 唯一索引或者非唯一索引

索引添加

1
ALTER TABLE `tab1` ADD INDEX `idx_index_name` USING BTREE (`field1`, `field2`) comment '';

平衡二叉树(Balanced Binary Tree)

性质

  1. 它是一棵空树或它的左右两个子树的高度差的绝对值不超过1
  2. 并且左右两个子树都是一棵平衡二叉树。

常用算法

平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等

BTree,B+Tree

B树(即二叉搜索树)

  1. 所有非叶子结点至多拥有两个儿子(Left和Right)
  2. 所有结点存储一个关键字;
  3. 非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树;

B+树

  1. 所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字自小而大顺序链接
  2. 不可能在非叶子结点命中;
  3. 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
  4. 更适合文件索引系统;

总结

  • B树:二叉树,每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;
  • B-树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;
  • 所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;
  • B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
  • B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;

B+Tree索引实现原理

B+树概念

B+树是为磁盘或其他直接存取辅助设备而设计的一种平衡查找树,在B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶节点中,各叶节点指针进行连接。

B+树示意图:

B+树示意图

InnoDB数据页结构

组成部分:File Header(文件头)、Page Header(页头)、Infimun + Supremum Records、User Records(用户记录,即行记录)、Free Space(空闲空间)、Page Directory(页目录)、File Trailer(文件结尾信息)

  • 页是InnoDB存储引擎管理数据库的最小磁盘单位,InnoDB中的页大小为16KB,且不可以更改。

  • 叶子节点(页与页之间)是双向链表串起来的,头连上一页的尾,尾连下一页的头

    双向链表

  • 每个数据页中有两个虚拟的行记录,用来限定记录的边界。(行记录是记录在页中的,同时是在页内行记录之间也是双向链表链接的)

    虚拟行记录演示

  • 页目录(Page Directory)通过顺序排放的记录指针(也叫槽)存放了记录的相对位置,通过这些槽找到具体的数据位置

    页目录中存放了记录的相对位置,有些时候这些记录指针称为Slots(槽)或者目录槽,与其他数据库不同的是,InnoDB并不是每个记录拥有一个槽,InnoDB中的槽是一个稀疏目录,即一个槽中可能属于多个记录,最少属于4个目录,最多属于8个目录。槽中记录按照键顺序存放,这样可以利用二叉查找迅速找到记录的指针。但是由于InnoDB中的Slots是稀疏目录,二叉查找的结果只是一个粗略的结果,所以InnoDB必须通过recorder header中的next_record来继续查找相关记录。同时slots很好的解释了recorder header中的n_owned值的含义,即还有多少记录需要查找,因为这些记录并不包括在slots中

查询B+树索引的流程

首先通过B+树索引找到叶节点,再找到对应的数据页,然后将数据页加载到内存中,通过二分查找Page Directory中的槽,查找出一个粗略的目录,然后根据槽的指针指向链表中的行记录,之后在链表中依次查找。

注意:

B+树索引不能找到具体的一条记录,而是只能找到对应的页。把页从磁盘装入到内存中,再通过Page Directory进行二分查找,同时此二分查找也可能找不到具体的行记录(有可能会找到),只是能找到一个接近的链表中的点,再从此点开始遍历链表进行查找。

聚簇索引/非聚簇索引

区别:

  • 聚簇索引一般是为主键索引,一张表只能有一个聚簇索引,B+树的叶子节点存储了行数据和主键。
  • 非聚簇索引可以有多个,叶节点除了包含关键字键值外还包含聚集索引的主键

聚集索引

聚集索引是按每张表的主键构造的一颗B+树,并且叶节点中存放着整张表的行记录数据,因此也让聚集索引的节点成为数据页,这个特性决定了索引组织表中数据也是索引的一部分。由于实际的数据页只能按照一颗B+树进行排序,所以每张表只能拥有一个聚集索引。查询优化器非常倾向于采用聚集索引,因为其直接存储行数据,所以主键的排序查询和范围查找速度非常快。

不是物理上的连续,而是逻辑上的,不过在刚开始时数据是顺序插入的所以是物理上的连续,随着数据增删,物理上不再连续。

辅助索引

辅助索引页级别不包含行的全部数据。叶节点除了包含键值以外,每个叶级别中的索引行中还包含了一个书签,该书签用来告诉InnoDB哪里可以找到与索引相对应的行数据。其中存的就是聚集索引的键。

辅助索引的存在并不影响数据在聚集索引的结构组织。InnoDB会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后通过主键索引找到一个完整的行记录。当然如果只是需要辅助索引的值和主键索引的值,那么只需要查找辅助索引就可以查询出索要的数据,就不用再去查主键索引了

什么是一级索引、二级索引及生成场景

每个InnoDB表具有一个特殊的索引称为聚簇索引(也叫聚集索引,聚类索引,簇集索引)。

  • 如果表上定义有主键,该主键索引就是聚簇索引
  • 如果未定义主键,MySQL取第一个唯一索引(unique)而且只含非空列(NOT NULL)作为主键,InnoDB使用它作为聚簇索引。
  • 如果没有这样的列,InnoDB就自己产生一个这样的ID值,它有六个字节,而且是隐藏的,使其作为聚簇索引。

表中的聚簇索引(clustered index )就是一级索引,除此之外,表上的其他非聚簇索引都是二级索引,又叫辅助索引(secondary indexes)

MyISAM和InnoDB索引实现

原始数据如图:

原始数据

MyISAM引擎索引实现方式

索引实现原理

是按列值与行号来组织索引的。它的叶子节点中保存的实际上是指向存放数据的物理块的指针。从MYISAM存储的物理文件我们能看出,MYISAM引擎的索引文件(.MYI)和数据文件(.MYD)是相互独立的。

MyISAM引擎的数据存储方式

主键索引与二级索引

MyISAM的主键索引和二级索引叶子节点存放的都是:列值与行号的组合,叶子节点中保存的是数据的物理地址

MYISAM的主键索引和二级索引没有任何区别:主键索引仅仅只是一个叫做PRIMARY的唯一、非空的索引,且MYISAM引擎中可以不设主键

InnoDB引擎索引实现方式

1. 聚簇索引(一级索引)

聚簇索引中的每个叶子节点包含主键值、事务ID、回滚指针(rollback pointer用于事务和MVCC)和余下的列(如col2),如下图:

InnoDB存储的数据结构

2. 非聚簇索引(二级索引)

InnoDB的二级索引与主键索引有很大的不同,InnoDB的的二级索引的叶子节点存放的是:KEY字段加主键值,而不是行指针(row pointers),如下图

二级索引

二级索引查询步骤:先查到主键值,然后InnoDB再根据查到的主键值通过主键索引找到对应的数据块

INNODB和MYISAM的主键索引与二级索引的对比

如图:

INNODB和MYISAM对比图

参考资料:高性能MYSQL

参考

平衡树
BTree,B-Tree,B+Tree,B*Tree都是什么
MySQL有哪些索引类型 ?
mysql索引类型 normal, unique, full text
MySQL_innoDB索引底层原理详解
MYSQL性能调优: 对聚簇索引和非聚簇索引的认识
面试知识点6:MySQL中InnoDB的一级索引、二级索引