深入理解Mysql索引底层数据结构与算法

31次阅读

共计 2110 个字符,预计需要花费 6 分钟才能阅读完成。

索引是帮助 MySQL 高效获取数据的排好序的数据结构

索引数据结构对比

二叉树

左边子节点的数据小于父节点数据,右边子节点的数据大于父节点数据。
如果 col2 是索引,查找索引为 89 的行元素,那么只需要查找两次,就可以获取到行元素所在的磁盘指针地址。


如果 col1 是索引,查找索引为 6 的行元素,那么需要查找六次,就可以获取到行元素所在的磁盘指针地址,即得到了该索引为 6 的行元素。因此二叉树不适合存储单边增长的序列字段,近乎全表扫描获取数据。

红黑树

本质二叉树,属于二叉平衡树,jdk1.8 hashmap 的底层实现;
存储大数据量,树的高度不可控,数量越大,树的高度越高;
500w 行数据,2 的 n 次方 =500w 数据量,n 是树的高度,也就是查询次数;

hash 表

通过散列可以快速获取磁盘文件指针,对于指定索引查找文件非常快,但是对于范围查找没法支持。

B 树

本质是多路二叉树;
叶节点具有相同的深度,叶节点的指针为空;
所有索引元素不重复;
节点中数据索引从左到右依次递增的;

B+ 树(B 树的变种)

非叶子节点不存储数据,只存储索引 (冗余) 和指针,可以放更多的索引,树高降低;
叶子节点包含所有索引字段;
叶子节点比 b 树增加了指针连接;
叶子节点有双向指针链接(首尾子节点还通过指针连接),提高区间访问的性能,范围查找;

为什么 mysql 页文件默认 16K?

MySQL 每个 B + 树节点最大存储容量:16KB(指针 + 数据 + 索引)。假设我们一行数据大小为 1K,那么一页就能存 16 条数据,也就是一个叶子节点能存 16 条数据;再看非叶子节点,假设主键 ID 为 bigint 类型,那么长度为 8B,指针大小在 Innodb 源码中为 6B,一共就是 14B,那么一页里就可以存储 16K/14=1170 个 (主键 + 指针)
那么一颗高度为 2 的 B + 树能存储的数据为:117016=18720 条,一颗高度为 3 的 B + 树能存储的数据为:11701170*16=21902400(千万级条)

show global status like `Innodb_page_size`

因此,B+ 树存储大数据量的表也可以非常高效的获取数据,MySQL 使用 B + 树作为索引的数据结构。

存储引擎

存储引擎最终作用于:表,不是数据库
在 mysql 的安装的根目录下,有一个 data 目录,里面存放的是所有表的数据。

  • MyISAM:

MyISAM 索引文件和数据文件是分离的(非聚集或稀疏);
主键索引和辅助主键索引存储类似;

frm 文件:存储这张表的表结构
MYD 文件:存储这张表的所有数据行
MYI 文件:存储这张表的索引字段

  • InnoDB(聚集):

表数据文件本身是按照 B +tree 组织的一个索引结构文件
frm 文件:存储这张表的表结构
ibd 文件:存储这张表的所有数据行和索引字段
聚集 (聚簇) 索引 —- 叶节点包含完整数据记录

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

  • 首先,为了满足 MySQL 的索引数据结构 B + 树的特性,必须要有索引作为主键,可以有效提高查询效率,因此 InnoDB 必须要有主键。如果不手动指定主键,InnoDB 会从插入的数据中找出不重复的一列作为主键索引,如果没找到不重复的一列,InnoDB 会在后台增加一列 rowId 做为主键索引。
  • 其次,索引的数据类型是整型,一方面整型占有的磁盘空间或内存空间相比字符串更少,另一方面整型比较比字符串比较更快速,字符串比较是先转换为 ASCII 码,然后再比较的。
  • 最后,B+ 树本质是多路多叉树,如果主键索引不是自增的,那么后续插入的索引就会引起 B + 树的其他节点的分裂和重新平衡,影响数据插入的效率,如果是自增主键,只用在尾节点做增加就可以。

为什么非主键索引结构叶子节点存储的是主键值?

  • 主键索引和非主键索引维护各自的 B + 树结构,当插入的数据的时候,由于数据只有一份,通过非主键索引获取到主键值,然后再去主键索引的 B + 树数据结构中找到对应的行数据,节省了内存空间;
  • 如果非主键索引的叶子节点也存储一份数据,如果通过非主键索引插入数据,那么要向主键索引对应的行数据进行同步,那么会带来数据一致性问题。可以通过事务的方式解决,我们都知道使用事务后,就会对性能有所消耗。

联合索引

  • 联合索引的底层存储结构长什么样?

    定义联合索引(员工级别,员工姓名,员工出生年月),将联合索引按照索引顺序放入节点中,新插入节点时,先按照联合索引中的员工级别比较,如果相同会按照是员工姓名比较,如果员工级别和员工姓名都相同 最后是员工的出生年月比较。可以从图中从上到下,从左到右看,第一个 B + 树的节点 是通过联合索引的员工级别比较的,第二个节点是 员工级别相同,会按照员工姓名比较,第三个节点是 员工级别和员工姓名都相同,会按照员工出生年月比较。

还没关注我的公众号?

  • 扫文末二维码关注公众号【小强的进阶之路】可领取如下:
  • 学习资料:1T 视频教程:涵盖 Javaweb 前后端教学视频、机器学习 / 人工智能教学视频、Linux 系统教程视频、雅思考试视频教程;
  • 100 多本书:包含 C /C++、Java、Python 三门编程语言的经典必看图书、LeetCode 题解大全;
  • 软件工具:几乎包括你在编程道路上的可能会用到的大部分软件;
  • 项目源码:20 个 JavaWeb 项目源码。

正文完
 0