MySQL索引及其底层原理(下)

目录

前言

一、B+树的特点

二、聚簇索引与非聚簇索引

三、索引操作

主键索引

唯一索引

普通索引

复合索引

四、索引总结


前言

ok,紧接着就更新了我们mysql索引相关部分的下篇,在这篇中我们会学习到其底层所使用的B+树的特点以及两种索引类型,最后我们将接触到其实之前也了解过一些的索引操作,相信大家看完这两篇内容能对mysql中的索引部分有更深的认识!!

一、B+树的特点

如同上篇所说,索引是 B+树 的实际运用,这里我们两者结合再来回顾一下 B+树 的特点

叶子节点保存有数据,非叶子节点不要数据

  • 非叶子节点:不存数据,只存储目录项,可以存储更多的目录项。

  • 目录页:一个目录页可以管理更多的叶子 Page ,使树更 "矮胖" ,减少 I/O 次数,提高效率。

相比之下,B树 每一个节点内既包含目录项又包含数据,所以 B树 除了叶子节点有数据路上节点也会包含数据。

叶子节点全部用链表级联起来

  • 链表级联:叶子节点用链表级联,便于进行范围查找,提高查询效率。

索引结构

  • InnoDB的索引结构:MySQL InnoDB 存储引擎使用 B+树 作为索引结构,一般我们建表插入数据时就是在该结构下进行CURD操作

  • 主键索引:默认情况下,如果没有指定主键,MySQL 会自动生成一个隐藏列作为主键,一定会构建对应的B+树

  • 普通索引:用户可以建立其他列的索引,这些索引也是 B+ 树结构。

复盘

  • Page分类:Page分为目录页和数据页。目录页只放各个下级 Page 的最小键值。

  • 查找过程:自顶向下查找,只需加载部分目录页到内存,大大减少 I/O 次数。

  • 索引构建:构建索引就是在MySQL内存中构建 B+ 树,以指定列作为 key 值。

与其他数据结构的对比

  • 链表:线性遍历,效率低。

  • 二叉搜索树:可能退化成线性结构,效率不稳定。

  • AVL & 红黑树:虽然平衡,但树高较高,I/O次数多。

  • Hash:适合点查询,但在范围查找方面表现不佳。

想借助动图来理解B+树的话,请点击这里

二、聚簇索引与非聚簇索引

非聚簇索引

  • MyISAM存储引擎,索引和数据分离,适合某些场景。

  • MyISAM 引擎同样使用 B+树 作为索引结果,叶节点的 data域 存放的是数据记录的地址。下图为 MyISAM 表的主索引, Col1 为主键。

其中, MyISAM 最大的特点是,将索引Page和数据Page分离,也就是叶子节点没有数据,只有对应数据的地址。

相较于 InnoDB 索引, InnoDB 是将索引和数据放在一起的

聚簇索引

  • InnoDB存储引擎,数据和索引放在一起,提高查询效率。比如下图为 InnoDB存储 引擎的普通索引结构,其中Col3为索引列

两种方式建表产生的文件如下:

由此肉眼可见innodb就是聚簇索引,将索引和数据放一起,而myisam则是非聚簇索引,将索引和数据分开放

当然,MySQL 除了默认会建立主键索引外,我们用户也有可能建立按照其他列信息建立的索引,一般这种索引可以叫做辅助(普通)索引。

对于 MyISAM,建立辅助(普通)索引和主键索引没有差别,无非就是主键不能重复,而非主键可重复。

下图就是基于 MyISAM 的 col2 建立的索引,和主键索引没有差别

同样, InnoDB 除了主键索引,用户也会建立辅助(普通)索引,我们以上表中的 Col3 建立对应的辅助索引如下图

可以看到, InnoDB 的非主键索引中叶子节点并没有数据,而是只有对应记录的key值

所以通过辅助(普通)索引,找到目标记录,需要两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。这种过程,就叫做回表查询,为何 InnoDB 针对这种辅助(普通)索引的场景,不给叶子节点也附上数据呢?原因就是太浪费空间了

请注意:

  • 所以一张表没有指明任何主键,mysql 默认会给表添加默认主键也会以 B+ 树结构呈现,只不过我没有设立主键就只能线性遍历。

  • 如果我们指明主键默认我们的表会配上主键索引,会以我们自己设置的主键为 key值 设立主键索引。

  • 如果我们指明主键索引,未来还想给其他列设置索引,我们可以手动添加。

  • 添加之后会在 mysql 内部重新构建 B+ 树,以 MyISAM 为例会指向记录,如果是 InnoDB 保存的是主键值方便我们快速索引。换句话说,一个表可能会建立主键索引或者其他普通索引,不管建立任何索引最终在 mysql 中一张表可能会有多颗 B+ 树。

  • 索引语法上分三类:主键索引、唯一键索引、普通索引,但其实宏观上就两类一个是主键索引 ,指明就用主键的没有指明就用默认的。一个是普通索引,包括唯一键索引

三、索引操作

主键索引

创建主键索引

第一种方式

  • 在创建表时直接指定 primary key
sql 复制代码
create table test1(id int primary key, name varchar(30));

说明:在字段名后指定 primary key,MySQL会根据该列构建主键索引。

第二种方式

  • 在创建表的最后指定某列或某几列为主键索引
sql 复制代码
create table test2(id int, name varchar(30), primary key(id));

说明:在表定义的最后指定某列或某几列为主键。

第三种方式

  • 创建表以后再添加主键
sql 复制代码
create table test3(id int, name varchar(30));
alter table test3 add primary key(id);

说明:先创建表,再通过 alter table 添加主键。

主键索引的特点:

  • 一个表中,最多有一个主键索引,当然可以是复合主键(约束的列)

  • 主键索引的效率高(主键不可重复)

  • 创建主键索引的列,它的值不能为 null,且不能重复

  • 主键索引的列基本上是 int

查询索引

第一种方法

  • 使用 show keys from 表名
sql 复制代码
show keys from test1;

第二种方法

  • 使用 show index from 表名
sql 复制代码
show index from test1;

第三种方法

  • 使用 desc 表名
sql 复制代码
desc test1;

Key_name 索引名称 | Column_name 索引列 | Index_type 索引类型

删除索引

删除主键索引

  • 第一种方法
sql 复制代码
alter table 表名 drop primary key;

删除其他索引

  • 第一种方法
sql 复制代码
alter table 表名 drop index 索引名;
  • 第二种方法
sql 复制代码
drop index 素引名 on 表名;

唯一索引

创建

第一种方式

  • 在表定义时指定 unique 唯一属性
sql 复制代码
create table test4(id int primary key, name varchar(30) unique);

第二种方式

  • 在表定义的最后指定某列或某几列为 unique
sql 复制代码
create table test5(id int primary key, name varchar(30), unique(name));

第三种方式

  • 创建表以后再添加唯一键
sql 复制代码
create table test6(id int primary key, name varchar(30));
alter table test6 add unique(name);

添加唯一键后,表中会有两个 B+树 ,一个是主键索引,另一个是以指定列构建的唯一索引。

删除

使用 alter table 删除索引

sql 复制代码
alter table 表名 drop index 索引名;

使用drop index 删除索引

sql 复制代码
drop index 索引名 on 表名;

唯一索引的删除方式与普通索引相同,不能使用 drop unique。

  • 删除唯一键不能用drop unique,用的是drop index。

  • 来你会发现我们删除普通索引用的也是drop index。

  • 说明unique索引本身也是一个普通索引。只不过指明它是unique是为了照顾表中的约束关系。

  • 其实在索引层面,普通索引和唯一键索引都是一般索引。

  • 最特殊的就是主键索引。

对此,我们可以对唯一索引总结出以下特点

  • 一个表中可以有多个唯一索引(唯一是指 无重复数据)

  • 查询效率高

  • 如果在某一列建立唯一索引,必须保证这列不能有重复数据

  • 如果一个唯一索引上指定 not null,等价于主键索引

普通索引

创建

第一种方式

  • 在表定义的最后指定某列为索引
sql 复制代码
create table test8(
    id int primary key,
    name varchar(20),
    email varchar(30),
    index(name)
);

第二种方式

创建完表以后指定某列为普通索引

sql 复制代码
create table test9(
    id int primary key,
    name varchar(20),
    email varchar(30)
);
alter table test9 add index(name);

第三种方式

创建一个索引名为 idx_name 的索引

sql 复制代码
create table test10(
    id int primary key,
    name varchar(20),
    email varchar(30)
);
create index idx_name on test10(name);

如果某列需要创建索引,但该列有重复值,应使用普通索引

复合索引

创建

sql 复制代码
create table test11(
    id int primary key,
    name varchar(20),
    email varchar(30)
);
create index idx_name_email on test11(name, email);
  • 创建复合索引:我们以 name 和 email 两列建立索引。

  • 索引数量:创建复合索引后,表中显示有三个索引,但这并不意味着新增了两个B+树。

  • 单一B+树:复合索引实际上只构建了一颗B+树,而不是两颗。

  • 键值组合:这颗B+树的键值是由 name 和 email 两列值组合而成。

  • 搜索条件:在搜索时,这两列必须同时满足条件才能找到目标记录。

至于什么时候使用复合索引呢?

  • 避免回表:InnoDB普通索引的叶子节点放的是表的主键的key值,这意味着需要回表查询。但如果以 name 和 email 构建复合索引,未来高频查询时,可以直接通过 name 和 email 查找,数据本身就在这颗复合索引的B+树里,无需回表。

  • 索引覆盖:如果查询条件和返回值都在复合索引的列中,可以直接从索引中获取数据,无需回表,这种情况称为索引覆盖,覆盖的是主键索引。

  • 最左匹配原则:MySQL在索引匹配时是从左侧开始向右匹配。例如,可以按 name 或 name 和 email 查找,但不能直接按 email 查找。

四、索引总结

  1. 频繁作为查询条件的字段:应该创建索引。

  2. 唯一性太差的字段:不适合单独创建索引,即使频繁作为查询条件。 示例:给性别打上索引,但性别只有男和女,构建出的B+树并不优秀。

  3. 更新非常频繁的字段:不适合作创建索引。 示例:考试信息更改太频繁,索引创建出来是为了方便查询,频繁修改不仅影响数据,还会调整索引结构。

  4. 不会出现在 where 子句中的字段:不应创建索引。 示例:某些字段从未在 where 子句中出现,创建索引没有意义。

其实还有一个全文索引,用于在长文本中查找特定的内容,而不仅仅是找到一条记录。但是这个就交给大家自行了解了!

相关推荐
e***8908 小时前
MySQL 8.0版本JDBC驱动Jar包
数据库·mysql·jar
p***199412 小时前
MySQL——内置函数
android·数据库·mysql
橘颂TA13 小时前
【MySQL】90% 开发者都踩过的坑:数据库数据类型选错有多可怕?
数据库·mysql·算法与结构
认真的薛薛16 小时前
数据库-MHA-读写分离-redis
数据库·sql·mysql
YIN_尹16 小时前
【MySQL】数据处理的瑞士军刀——常用的内置函数(下)
android·mysql
霖霖总总17 小时前
[小技巧70]深入COUNT(*)、COUNT(1) 与 COUNT(字段):你以为的快,其实慢了
数据库·mysql
为自己_带盐19 小时前
从零开始玩转 Microsoft Agent Framework:我的 MAF 实践之旅第三篇—工作流
数据库·mysql·microsoft
旺仔Sec1 天前
手把手教你从零搭建 Hive 2.3.6 + MySQL 元数据存储(含视频教程)
hive·hadoop·mysql
闻哥1 天前
MySQL 核心性能优化:预读机制与 LRU 冷热数据分离深度解析
java·数据库·spring boot·mysql·adb·面试·性能优化
计算机学姐1 天前
基于SpringBoot的二次元手办与周边交易商城系统【个性化推荐+数据可视化】
java·spring boot·后端·mysql·spring·信息可视化·推荐算法