MYSQL—DAY1

为什么 InnoDB 使用 B+ 树作为索引,而不是 B 树、红黑树或 Hash?

InnoDB 使用 B+ 树,主要是因为数据库索引需要持久化到磁盘中,而磁盘随机 I/O 的成本很高,所以索引结构需要尽可能降低树的高度和磁盘页访问次数。

B+ 树是一种多叉平衡树,一个磁盘页可以存储大量索引键和子节点指针,因此分支数量多,树通常只有 3~4 层,查询时需要访问的磁盘页较少。

B 树的非叶子节点也存储数据,在固定磁盘页大小下,每个节点能容纳的索引键更少,导致分叉数更少 ,因此相同数据量下树高通常会高于 B+ 树。B+ 树非叶子节点只存索引键和子节点指针,扇出更大,可以减少磁盘 I/O。同时,所有记录都位于叶子节点,叶子节点按索引值有序排列并通过链表连接,非常适合范围查询、排序和顺序扫描。

红黑树属于二叉树,节点分支少,数据量较大时树高较高,如果用于磁盘索引,会产生更多磁盘页访问。

Hash 索引虽然适合等值查询,但数据是无序的,不支持高效的范围查询、排序和联合索引的最左前缀匹配。

跳表更适合内存场景,例如 Redis。它会产生较多指针跳转和随机内存访问,但 Redis 数据主要存储在内存中,因此不存在普通磁盘索引所关注的大量随机磁盘 I/O 问题。如果跳表用于磁盘索引,分散的节点访问反而可能导致较多随机 I/O。

因此,综合磁盘 I/O、树高、范围查询和排序等需求,InnoDB 选择 B+ 树作为主要索引结构。

什么是聚簇索引、二级索引、回表和覆盖索引?

假设表是:

sql 复制代码
CREATE TABLE user (
    id INT PRIMARY KEY,
    name VARCHAR(20),
    age INT,
    INDEX idx_age(age)
);

聚簇索引的叶子节点存什么?

主键索引 id 是聚簇索引,它的叶子节点存放的是:id + name + age

二级索引的叶子节点存什么?

idx_age(age) 是二级索引,它的叶子节点存放的是:age + 主键 id

聚簇索引是什么?

聚簇索引就是叶子节点直接存放完整一行数据的索引。在 InnoDB 中,主键索引通常就是聚簇索引,所以通过主键查询时,找到索引就能直接拿到完整数据。

二级索引是什么?

二级索引就是除聚簇索引之外的普通索引。它的叶子节点不存完整数据,只存"索引字段和主键值"。

回表是什么?

使用二级索引查询时,先通过二级索引找到主键,再根据主键去聚簇索引中查询完整数据,这个再次查询的过程就叫回表。

覆盖索引是什么?

如果查询需要的字段在二级索引中已经全部存在,就可以直接返回结果,不需要再去聚簇索引中查询,这就叫覆盖索引。

例如:CREATE INDEX idx_age_name ON user(age, name);

SELECT name FROM user WHERE age = 20; 不需要回表,是覆盖索引

什么是联合索引的最左前缀原则?为什么 (A,B,C) 索引下,WHERE B=? AND A=? AND C=? 仍然可以走索引,而 A=? OR B=? 可能不行?

联合索引 (A,B,C) 是先按 A 排序,A 相同时按 B 排序,A、B 都相同时再按 C 排序,所以索引使用一般要从最左列 A 开始。

最左前缀原则与 WHERE 条件的书写顺序无关,因此 B=? AND A=? AND C=? 中同时存在 A、B、C,优化器可以按照索引顺序使用完整联合索引。

而 A=? OR B=? 中,AND 中可以先确定 A,再查 B;OR 中满足 B 的数据不一定满足 A,所以 B 这一支缺少最左列 A,可能无法使用 (A,B,C) 联合索引。

查询条件 索引使用情况
A = ? 可以使用 A
A = ? AND B = ? 可以使用 A、B
A = ? AND B = ? AND C = ? 可以使用 A、B、C
B = ? AND A = ? 可以使用 A、B,条件顺序无影响
B = ? 通常不能有效使用联合索引定位
A = ? AND C = ? A 可用于定位,C 通常不能连续用于定位
A > ? AND B = ? A 用于范围定位,B 通常不能继续缩小索引扫描范围
A = ? OR B = ? 可能无法使用该联合索引,或使用索引合并

哪些情况可能导致索引无法高效使用?

重点包括:前置模糊查询、对索引列使用函数或计算、隐式类型转换、联合索引缺少最左列、范围查询影响后续索引列。

  1. 联合索引不满足最左前缀原则;

  2. 对索引列使用函数或计算;

  3. 索引字段发生隐式类型转换;

  4. 关联字段的字符集或排序规则不同,导致隐式转换;

  5. .LIKE 查询以 % 开头;

  6. OR的混用;

  7. 使用 !=、NOT IN、NOT LIKE 等否定条件;

  8. 查询返回的数据量过大或回表次数过多,优化器认为全表扫描成本更低。

不满足最左前缀原则

联合索引:

sql 复制代码
INDEX idx_abc(A, B, C)
sql 复制代码
WHERE B = 2;

缺少最左列 A,通常不能高效使用联合索引。

联合索引中间断开

sql 复制代码
WHERE A = 1 AND C = 3;

可以使用 A,但跳过了 B,C 通常不能继续用于索引定位。


对索引列使用函数或计算

sql 复制代码
WHERE YEAR(create_time) = 2026;
WHERE age + 1 = 20;

可以改成:

sql 复制代码
WHERE create_time >= '2026-01-01'
  AND create_time < '2027-01-01';

WHERE age = 19;

发生隐式类型转换

phone 是 VARCHAR:

sql 复制代码
WHERE phone = 13800138000;

应写成:

sql 复制代码
WHERE phone = '13800138000';

否则可能对索引列进行类型转换。


LIKE 以 % 开头

sql 复制代码
WHERE name LIKE '%张%';

通常不能高效使用普通 B+ 树索引。

sql 复制代码
WHERE name LIKE '张%';

通常可以使用索引。


OR 中一边没有可用索引

联合索引是 (A,B,C):

sql 复制代码
WHERE A = 1 OR B = 2;

A=1 可以走索引,OR 中满足 B 的数据不一定满足 A,所以 B 这一支缺少最左列 A,可能无法使用 (A,B,C) 联合索引,所以整体可能全表扫描。


使用否定条件

sql 复制代码
WHERE age != 20;
WHERE id NOT IN (1, 2, 3);
WHERE name NOT LIKE '张%';

查询结果太多或回表太多

sql 复制代码
SELECT *
FROM user
WHERE gender = 1;

如果全表大部分数据都是 gender=1,走索引需要大量回表,MySQL 可能直接全表扫描。

如何使用 EXPLAIN 判断 SQL 是否走索引?

重点掌握:type、possible_keys、key、key_len、rows、Extra,以及 Using index 和 Using index condition 的区别。

使用 EXPLAIN 查看 SQL 执行计划,主要看这几个字段:

  1. possible_keys :可能使用的索引;为 NULL 表示没有合适的索引。

  2. key :实际使用的索引;key 不为 NULL,说明走了索引。

  3. type:查询方式,常见性能由好到差为:

    const > eq_ref > ref > range > index > ALL

其中 ALL 是全表扫描,index 是全索引扫描。

  1. rows:预计扫描的行数,通常越少越好。
  2. Extra :额外信息,Using index 表示覆盖索引;Using filesort 表示额外排序;Using temporary 表示使用临时表。

一句话记忆:看 key 判断有没有走索引,看 type、rows 和 Extra 判断索引走得好不好。

重点看这些字段:

  1. possible_keys = idx_tap_user_classname
    表示优化器认为 classname 上的索引可以使用。
  2. key = idx_tap_user_classname
    表示最终确实选择了这个索引。判断是否走索引,主要看 key 是否为 NULL。
  3. type = ref
    表示通过普通非唯一索引进行等值查询,属于比较好的访问方式。
  4. ref = const
    表示拿一个固定值:计科25转专业同学C语言去索引中查找。
  5. rows = 1
    优化器预计只需要扫描一条记录,说明这个条件的区分度较好。
  6. filtered = 100%
    表示扫描到的记录预计全部符合条件,不需要再过滤大量数据。
  7. Extra = NULL
    没有出现 Using filesort、Using temporary 等额外操作。
相关推荐
wuyk55530 分钟前
《WiFi 嵌入式物联网开发全套实战》 | 第 45 章【终章】WiFi 开发全套知识复盘 + 面试高频题 + 进阶路线
物联网·面试·职场和发展
andrsted42 分钟前
练题簿免费刷题小程序:和学习搭子共用一份题库,一起刷题复习
学习·面试·微信小程序·小程序·学习方法·刷题
丑陋小蚊子1 小时前
工作日不方便接电话,简历要注明联系时间吗?
面试·求职招聘·大学生·简历·简历下载
凯瑟琳.奥古斯特4 小时前
OCR文字检测算法对比解析
开发语言·python·职场和发展
Rain的Java大神之路5 小时前
MySQL 被高并发打爆?从定位到分库分表逐层拆解(含秒杀库存实战)
java·后端·面试
背景调查知识库5 小时前
职业背景调查中的职业事实与主观评价分离如何判断?
职场和发展
彭亚川Allen6 小时前
从零手搓 RAG 之分块策略
前端·后端·面试
利威尔xu7 小时前
Go GMP 调度器 10 个高频追问:3 个线上故障让 P99 从 80ms 飙到 2s、10 万 goroutine 泄漏
开发语言·后端·面试·性能优化·golang
Interview Aid1121 天前
Snowflake OA 面试真题:前缀和、BFS 与动态规划
面试·动态规划·宽度优先
学顶教育1 天前
学顶教育:公务员面试常见问题!
面试·职场和发展