MySQL 索引深入剖析(二)

109 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第32天,点击查看活动详情

B+树(加强版多路平衡查找树)

B Tree 的效率已经很高了,为什么 MySQL 还要对 B Tree 进行改良,最终使用了B+Tree 呢?

总体上来说,这个 B 树的改良版本解决的问题比 B Tree 更全面。

我们来看一下 InnoDB 里面的 B+树的存储结构:

image.png

MySQL 中的 B+Tree 有几个特点:

1、它的关键字的数量是跟路数相等的;

2、B+Tree 的根节点和枝节点中都不会存储数据,只有叶子节点才存储数据。

目前的认知:我们这要存放的数据是什么?是不是真实数据的地址?

搜索到关键字不会直接返回,会到最后一层的叶子节点。比如我们搜索 id=28,虽然在第一层直接命中了,但是数据地址在叶子节点上面,所以我还要继续往下搜索,一直到叶子节点。

3、B+Tree 的每个叶子节点增加了一个指向相邻叶子节点的指针,它的最后一个数据会指向下一个叶子节点的第一个数据,形成了一个有序链表的结构。

InnoDB 中的 B+Tree 这种特点带来的优势

1)它是 B Tree 的变种,B Tree 能解决的问题,它都能解决。B Tree 解决的两大问题是什么?(每个节点存储更多关键字;路数更多)

2)扫库、扫表能力更强(对表进行全表扫描,只需要遍历叶子节点就可以了,不需要遍历整棵 B+Tree 拿到所有的数据)

  1. B+Tree 的磁盘读写能力相对于 B Tree 来说更强(根节点和枝节点不保存数据区,所以一个节点可以保存更多的关键字,一次磁盘加载的关键字更多)

4)排序能力更强(因为叶子节点上有下一个数据区的指针,数据形成了链表)

5)效率更加稳定(B+Tree 永远是在叶子节点拿到数据,所以 IO 次数是稳定的)

索引方式:真的是用的 B+Tree 吗?

在 Navicat 的工具中,创建索引,索引方式有两种。

HASH:以 KV 的形式检索数据,也就是说,它会根据索引字段生成哈希码和指针,指针指向数据。

image.png

哈希索引有什么特点呢?

第一个,它的时间复杂度是 O(1),查询速度比较快。但是哈希索引里面的数据不是按顺序存储的,所以不能用于排序。

第二个,我们在查询数据的时候要根据键值计算哈希码,所以它只能支持等值查询(= IN),不支持范围查询(> < >= <= between and)。

第三:如果字段重复值很多的时候,会出现大量的哈希冲突(采用拉链法解决),效率会降低。

需要注意的是,InnoDB,不能显示地创建一个哈希索引(所谓的支持哈希索引指的是 AHI)。

memory 存储引擎可以使用 Hash 索引。

CREATE TABLE `user_memory` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`name` varchar(255) DEFAULT NULL,

`gender` tinyint(1) DEFAULT NULL,

`phone` varchar(11) DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `idx_name` (`name`) USING HASH

) ENGINE=MEMORY AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

因为B Tree 和B+Tree 的特性,它们广泛地用在文件系统和数据库中,例如Windows的 HPFS 文件系统,Oracel、MySQL、SQLServer 数据库。

B+Tree 落地形式

MySQL 数据存储文件

上一节课我们知道了不同的存储引擎文件不一样

show VARIABLES LIKE 'datadir';

每 张 InnoDB 的 表 有 两 个 文 件 (.frm 和 .ibd ) , MyISAM 的 表 有 三 个 文 件(.frm、.MYD、.MYI)。

image.png

MyISAM

在 MyISAM 里面,另外有两个文件:

一个是.MYD 文件,D 代表 Data,是 MyISAM 的数据文件,存放数据记录,比如我们的 user_myisam 表的所有的表数据。

一个是.MYI 文件,I 代表 Index,是 MyISAM 的索引文件,存放索引,比如我们在id 字段上面创建了一个主键索引,那么主键索引就是在这个索引文件里面。

也就是说,在 MyISAM 里面,索引和数据是两个独立的文件。

那我们怎么根据索引找到数据呢?

MyISAM 的 B+Tree 里面,叶子节点存储的是数据文件对应的磁盘地址。所以从索引文件.MYI 中找到键值后,会到数据文件.MYD 中获取相应的数据记录。

image.png

如果是辅助索引,有什么不一样呢?

ALTER TABLE user_innodb DROP INDEX index_user_name;

ALTER TABLE user_innodb ADD INDEX index_user_name (name);

在 MyISAM 里面,辅助索引也在这个.MYI 文件里面。

辅助索引跟主键索引存储和检索数据的方式是没有任何区别的,一样是在索引文件里面找到磁盘地址,然后到数据文件里面获取数据。

image.png

这个就是 MyISAM 里面的索引落地的形式。但是在我们的 InnoDB 里面是不一样的。我们来看一下。

InnoDB

在 InnoDB 里面,它是以主键为索引来组织数据的存储的,所以索引文件和数据文件是同一个文件,都在.ibd 文件里面。

在 InnoDB 的主键索引的叶子节点上,它直接存储了我们的数据。

image.png

聚集索引(聚簇索引):就是索引键值的逻辑顺序跟表数据行的物理存储顺序是一致的。(比如字典的目录是按拼音排序的,内容也是按拼音排序的,按拼音排序的这种目录就叫聚集索引)。

在 InnoDB 里面,它组织数据的方式叫做叫做(聚集)索引组织表(clustered index organize table),所以主键索引是聚集索引,非主键都是非聚集索引。

主键之外的索引,比如在 name 字段上面建的普通索引,又是怎么存储和检索数据的呢?

image.png

InnoDB 中,主键索引和辅助索引是有一个主次之分的。

辅助索引存储的是辅助索引和主键值。如果使用辅助索引查询,会根据主键值在主键索引中查询,最终取得数据。

比如我们用 name 索引查询 name= 'zhangsan',它会在叶子节点找到主键值,也就是id=1,然后再主键索引的叶子节点拿到数据。

如果一张表没有主键怎么办?

dev.mysql.com/doc/refman/…

1、如果我们定义了主键(PRIMARY KEY),那么 InnoDB 会选择主键作为聚集索引。

2、如果没有显式定义主键,则 InnoDB 会选择第一个不包含有 NULL 值的唯一索引作为主键索引。

3、如果也没有这样的唯一索引,则 InnoDB 会选择内置 6 字节长的 ROWID 作为隐藏的聚集索引,它会随着行记录的写入而主键递增。

select _rowid name from t2;