📖 本篇简介:索引是 MySQL 面试的第一高频主题,几乎每一场面试都会从索引问起。本篇用 16 道高频面试题串起索引的全部基础考点:索引是什么、B+ 树底层原理、聚簇与非聚簇索引、回表与覆盖索引、最左前缀、索引代价等。学完本篇,你能完整、有条理地回答"索引"类基础问题,并接住面试官的层层追问。前置知识:会写基本的建表与 SELECT;本系列示例基于 MySQL 8.0。
目录
- 1 索引高频考点地图
- 题目 1 什么是索引底层为什么用 B+ 树
- 题目 2 聚簇索引与非聚簇索引的区别
- 题目 3 主键索引唯一索引普通索引的区别
- 题目 4 为什么 InnoDB 主键推荐用自增整数
- 题目 5 什么是回表什么是覆盖索引
- 题目 6 联合索引的最左前缀原则是什么
- 题目 7 一个 SQL 只能用一个索引吗
- 题目 8 索引能提升所有查询性能吗
- 题目 9 哈希索引和 B+ 树索引的区别
- 题目 10 全文索引与普通索引的区别
- 题目 11 索引下推是什么
- 题目 12 为什么建议索引选择性高的列
- 题目 13 NULL 值对索引的影响
- 题目 14 冗余索引与重复索引有什么区别
- 题目 15 为什么 B+ 树索引能扛住千万级数据
- 题目 16 MySQL 怎么决定要不要用索引
- 复习清单
- 练习题
1 索引高频考点地图
面试里"索引"类问题的常见问法,按难度分层:
| 难度 | 典型问题 | 本篇对应 |
|---|---|---|
| 入门 | 索引是什么?底层结构? | 题目 1 |
| 基础 | 聚簇/非聚簇?回表?覆盖索引? | 题目 2、5 |
| 进阶 | 最左前缀?索引下推?代价估算? | 题目 6、11、16 |
| 实战 | 主键选型?NULL?冗余索引? | 题目 4、13、14 |
一条主线记住:索引是"有序的数据结构 + 减少回表"的折中。所有追问都围绕"它怎么帮你少读数据、以及它为此付出了什么代价"展开。
题目 1:什么是索引?底层为什么用 B+ 树?
考点
- 索引的定义与本质:加速查询的数据结构;
- B+ 树与二叉树、B 树、哈希表的对比;
- InnoDB 索引页的组织方式。
参考答案
- 索引是对表中一列或多列的值进行排序的一种结构,目的就是减少扫描的数据量,让查找从"全表逐行"变成"沿树定位"。
- 底层用 B+ 树,而不是二叉树、B 树或哈希表:
1. 二叉树在数据量大时树高过高,且可能退化成链表,磁盘 IO 次数爆炸;
2. B 树每个节点既存索引又存数据,节点更大、单页能存的键更少,且非叶子节点也能命中,查找路径不稳定;
3. 哈希索引只能等值匹配,不支持范围查询和排序;
4. B+ 树只有叶子节点存数据,非叶子节点能存更多键(树更矮),且叶子节点之间用链表相连,天然支持范围查询和排序。
- InnoDB 默认页大小 16KB,一个 3 层的 B+ 树能存约千万到上亿行,3 次磁盘 IO 就能定位目标。
-- 验证索引效果:建索引前后查询计划对比(MySQL 8.0)
EXPLAIN SELECT * FROM user WHERE username = 'zhangsan';
-- type 从 ALL(全表扫描)变成 ref(走索引)
追问
- 问:B+ 树和 B 树在范围查询上谁更强?答:B+ 树叶子节点链表有序,范围查询只需定位起点后顺序遍历;B 树中序遍历会频繁在父子节点间跳转。
- 问:为什么不用跳表?答:跳表也是有序结构,但每个节点冗余指针多、缓存不友好,数据库领域 B+ 树是工程实践的主流选择。
题目 2:聚簇索引与非聚簇索引的区别?
考点
- 聚簇索引 = 主键索引,叶子存整行数据;
- 非聚簇索引(二级索引)叶子存主键值;
- 一张表只能有一个聚簇索引。
参考答案
- 聚簇索引(Clustered Index):InnoDB 中主键索引就是聚簇索引,叶子节点直接存放整行数据。表数据物理上按主键顺序组织。
- 非聚簇索引(Secondary Index,二级索引):叶子节点存放的是主键值(不是行数据)。通过二级索引查找,要先在索引树上找到主键,再回聚簇索引取行——这就是回表。
- 一张表只有一个聚簇索引(数据只有一份物理排列),但可以有多个二级索引。
- 引申:没有显式主键时,InnoDB 会找第一个非空唯一索引作为聚簇索引;都没有,则生成隐藏的
ROWID作为聚簇索引。
-- 演示:二级索引的叶子存主键值,查询需要回表
CREATE TABLE t_user (
id BIGINT PRIMARY KEY, -- 聚簇索引,叶子存整行
name VARCHAR(50),
KEY idx_name (name) -- 二级索引,叶子存 id
) ENGINE=InnoDB;
EXPLAIN SELECT * FROM t_user WHERE name = 'tom';
-- 走 idx_name(type=ref),Extra 若出现 Using index condition 说明有下推;无覆盖则回表
追问
- 问:如果二级索引能覆盖查询的所有列,还需要回表吗?答:不需要,这就是覆盖索引,Extra 显示
Using index。 - 问:主键改了会怎样?答:所有二级索引里存的主键值都要跟着改,这也是"主键越短越好"的原因。
题目 3:主键索引、唯一索引、普通索引的区别?
考点
- 三种索引的约束能力;
- 唯一性与 NULL 的处理差异。
参考答案
| 类型 | 唯一性 | NULL | 用途 |
|---|---|---|---|
| 主键索引 PRIMARY KEY | 唯一且非空 | 不允许 | 聚簇索引,表身份 |
| 唯一索引 UNIQUE KEY | 唯一 | 允许多个 NULL | 防止业务字段重复(手机号、订单号) |
| 普通索引 KEY/INDEX | 不限制 | 允许 | 加速查询 |
- 主键索引是非空唯一,一张表只有一个;唯一索引允许多个 NULL(MySQL 认为 NULL 互不相等,
UNIQUE列可存多个 NULL)。 - 唯一索引不仅能加速查询,还能在应用层漏判时兜底防重(写入重复会报错)。
-- 演示唯一索引兜底:重复插入报错
CREATE TABLE t_account (
id BIGINT PRIMARY KEY,
card_no CHAR(18) UNIQUE -- 身份证号唯一
);
INSERT INTO t_account VALUES (1, '110101199001010011');
-- 再次插入相同 card_no 会报错:Duplicate entry '110101199001010011' for key 'card_no'
追问
- 问:唯一索引列存了 NULL,还能再存一个 NULL 吗?答:可以,MySQL 的唯一索引允许多个 NULL(8.0 语义如此,与标准 SQL 有差异,值得在面试中主动点出)。
- 问:主键能用 UUID 吗?答:能用但很不推荐,见题目 4。
题目 4:为什么 InnoDB 主键推荐用自增整数?
考点
- 自增主键对 B+ 树插入顺序的影响;
- 页分裂与碎片;
- UUID 的随机性代价。
参考答案
- 自增主键插入时数据追加在 B+ 树最右叶子节点,顺序写,几乎不发生页分裂,写入性能高且无碎片;
- UUID/随机主键导致数据随机散落,频繁触发页分裂、页重组,写入变慢,索引膨胀(UUID 是 16 字节,比 BIGINT 的 8 字节大),二级索引里存的键也更大;
- 数字主键排序、比较、范围查询都更高效,还能方便地做分库分表的全局 ID 设计;
- 因此面试标准答案:自增 BIGINT 主键是 InnoDB 表的最佳实践(有业务含义的列如订单号也可以,但尽量不要用随机值)。
-- 推荐写法:自增 BIGINT 无符号主键
CREATE TABLE t_order (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '订单ID',
order_no VARCHAR(32) NOT NULL COMMENT '订单号',
UNIQUE KEY uk_order_no (order_no) -- 业务唯一靠唯一索引
) ENGINE=InnoDB;
追问
- 问:自增主键真的不会浪费吗?答:中间被删除的行不会复用 ID(默认 AUTO_INCREMENT 不回溯),这是特性不是 bug;批量插入可用
innodb_autoinc_lock_mode控制锁粒度。 - 问:分布式场景自增会撞吗?答:会,所以分布式一般用雪花 ID 或号段发号,本地库内仍按自增/雪花做主键。
题目 5:什么是回表?什么是覆盖索引?
考点
- 回表的定义与代价;
- 覆盖索引如何消除回表;
- Explain 中
Using index的含义。
参考答案
- 回表:二级索引只存主键值,用二级索引查到主键后,还要再回聚簇索引查整行,这第二次查找就叫回表。回表多一次随机 IO。
- 覆盖索引:查询所需的所有列都包含在同一个二级索引里,不需要回表,Extra 显示
Using index。 - 优化手段:把"查询列 + WHERE 条件列 + ORDER BY 列"设计进同一个联合索引,让查询"用索引即可完成"。
-- 回表 vs 覆盖索引对比
CREATE TABLE t_user (
id BIGINT PRIMARY KEY,
name VARCHAR(50),
age INT,
KEY idx_name_age (name, age)
);
-- 情况一:要的列只有 id(索引里有),Extra = Using index,不回表
EXPLAIN SELECT id, name FROM t_user WHERE name = 'tom';
-- 情况二:要 age(联合索引第二列,也在索引内),仍不回表
EXPLAIN SELECT id, name, age FROM t_user WHERE name = 'tom';
-- 情况三:要多查一个索引外的列(如 phone),必须回表
EXPLAIN SELECT id, name, phone FROM t_user WHERE name = 'tom';
追问
- 问:覆盖索引是不是越多越好?答:不是,联合索引本身有维护成本,覆盖索引是"查询模式稳定"时才值得设计。
- 问:
Using index condition和Using index有什么区别?答:前者是索引下推(回表前先过滤),后者是完全覆盖不回表,两者不同,别答混。
题目 6:联合索引的最左前缀原则是什么?
考点
- 联合索引的排序规则(先按第一列,再按第二列……);
- 哪些条件能走索引;
- 边界:范围查询、跳过前导列。
参考答案
- 联合索引
(a, b, c)相当于建立了(a)、(a,b)、(a,b,c)三套索引(最左前缀)。 - 必须从第一列开始连续命中,才能用到索引;跳过第一列只查
b或c时,索引失效(或只能部分使用)。 - 遇到范围查询(
>、<、BETWEEN等)的列之后,后续列无法用于索引定位(但 8.0 下 ICP 可做索引内过滤)。
-- 联合索引 (a,b,c) 的命中规则
CREATE TABLE t_log (
id BIGINT PRIMARY KEY,
a INT, b INT, c INT,
KEY idx_abc (a, b, c)
);
EXPLAIN SELECT * FROM t_log WHERE a = 1 AND b = 2 AND c = 3; -- ✅ 全用
EXPLAIN SELECT * FROM t_log WHERE a = 1 AND c = 3; -- ✅ 用 a,c 靠 ICP 过滤(8.0)
EXPLAIN SELECT * FROM t_log WHERE b = 2 AND c = 3; -- ❌ 跳过 a,全表扫描
EXPLAIN SELECT * FROM t_log WHERE a > 1 AND b = 2; -- ⚠️ a 用范围,b 大概率用不上定位
追问
- 问:联合索引列的顺序怎么排?答:等值条件放前面,选择性高的放前面,范围条件尽量放最后,兼顾排序需求。
- 问:
a = 1 AND b > 2 AND c = 3能用上 c 吗?答:不能用来定位(b 是范围后 c 失序),但 8.0 ICP 可在索引内继续过滤 c,减少回表。
题目 7:一个 SQL 只能用一个索引吗?
考点
- 单表查询与多个索引的关系;
- 索引合并(Index Merge)机制。
参考答案
- 一个 SQL 执行时通常只选一个索引(优化器选代价最小的),但 MySQL 也有索引合并(Index Merge)机制:
OR连接的多个等值条件可以同时用多个索引取交集(Intersection)/并集(Union),再用回表。 - 出现
Using intersect/union(sort_union)表示走了索引合并。索引合并往往是"优化器被逼无奈",多数场景不如设计一个联合索引。
-- OR 触发索引合并
CREATE TABLE t_user (id BIGINT PRIMARY KEY, name VARCHAR(50), email VARCHAR(100),
KEY idx_name (name), KEY idx_email (email));
EXPLAIN SELECT * FROM t_user WHERE name = 'a' OR email = 'b@c.com';
-- type=index_merge, Extra=Using union(idx_name,idx_email)
追问
- 问:OR 两个索引列,性能一定好吗?答:不一定,合并后还要回表去重,数据量大时不如
UNION ALL改写或联合索引。 - 问:什么时候该把 OR 改写成 UNION ALL?答:两个分支都命中索引、结果集都不小、回表量大时,改写往往更稳。
题目 8:索引能提升所有查询性能吗?
考点
- 索引的代价:写入变慢、占用空间、内存缓存压力;
- 优化器要权衡的"回表成本"。
参考答案
- 不能,索引有代价:
1. 写入变慢:每次 INSERT/UPDATE/DELETE 都要同步维护所有索引的 B+ 树;
2. 空间占用:每个索引都是一棵 B+ 树,占磁盘和缓冲池内存;
3. 查询未必变快:如果命中行数占比太高(比如选择性很差的列),优化器会认为回表成本高于全表扫描,放弃索引。
- 经验法则:单表索引数量控制在5~6 个以内;选择性 < 20% 的列慎建索引。
-- 优化器放弃索引的例子:status 只有 0/1,选择性差
CREATE TABLE t_order (id BIGINT PRIMARY KEY, status TINYINT, KEY idx_status (status));
EXPLAIN SELECT * FROM t_order WHERE status = 1;
-- 若表中大部分行 status=1,优化器会直接 type=ALL 全表扫描
追问
- 问:怎么判断"命中比例太高"?答:看 rows 预估与表总行数比例,超过 15%~20% 优化器倾向全表扫描;用
ANALYZE TABLE更新统计信息后再 EXPLAIN。 - 问:写多读少的表要索引吗?答:只给高频查询建最小必要索引,其余能省则省。
题目 9:哈希索引和 B+ 树索引的区别?
考点
- 哈希索引的适用场景;
- InnoDB 的"自适应哈希索引"(AHI)。
参考答案
- 哈希索引:等值查找 O(1),但不支持范围查询、排序、前缀匹配,也无法用于 ORDER BY / GROUP BY;哈希冲突时退化。
- MySQL 中:
- Memory 引擎支持显式 HASH 索引;
- InnoDB 不开放手工建哈希索引,但它有自适应哈希索引(Adaptive Hash Index, AHI)——当某个值频繁等值命中时,InnoDB 自动在 B+ 树之上加一层哈希加速,对用户透明。
- B+ 树索引:支持等值、范围、排序、前缀,是 InnoDB 的通用选择。
-- Memory 引擎哈希索引示例(学习用,生产首选 InnoDB)
CREATE TABLE t_cache (
key_name VARCHAR(64) PRIMARY KEY,
val VARCHAR(255)
) ENGINE=MEMORY; -- 默认即 HASH,等值极快,重启数据丢失
追问
- 问:Redis 的哈希和 MySQL 的哈希索引一回事吗?答:数据结构思想一样(哈希桶),但 Redis 是内存键值,MySQL 索引要落盘、要事务一致性,实现完全不同。
- 问:AHI 需要配置吗?答:8.0 默认开启,
innodb_adaptive_hash_index=ON,一般不动。
题目 10:全文索引与普通索引的区别?
考点
- 全文索引的适用场景(LIKE '%x%' 的替代);
- 倒排索引概念;
- 8.0 默认分词器 ngram。
参考答案
- 普通索引适合等值、范围、前缀匹配,
LIKE '%关键词%'这种中间模糊匹配走不了普通索引; - 全文索引(FULLTEXT)解决全文检索:底层是倒排索引(词 → 文档列表),适合搜索场景;MySQL 8.0 用 ngram 分词器支持中文。
-- 全文索引示例(MySQL 8.0,ngram 支持中文)
CREATE TABLE t_article (
id BIGINT PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT KEY ft_body (body) WITH PARSER ngram
) ENGINE=InnoDB;
-- 自然语言模式搜索(替代 LIKE '%优化%')
SELECT id, title FROM t_article
WHERE MATCH(body) AGAINST('索引优化' IN NATURAL LANGUAGE MODE);
追问
- 问:为什么不用 LIKE?答:
%关键字%无法用 B+ 树索引,数据量大时全表扫描,全文索引用倒排表定位词,快得多。 - 问:全文索引和 ES 的区别?答:MySQL 全文索引适合中小规模站内搜索;大规模、高并发搜索建议 Elasticsearch 等专业检索引擎。
题目 11:索引下推是什么?
考点
- ICP(Index Condition Pushdown)的定义;
- 减少回表行数的原理;
- Extra 中
Using index condition的识别。
参考答案
- 索引下推(ICP, Index Condition Pushdown):MySQL 5.6+ 优化,把 WHERE 中能由索引列判断的条件下推到存储引擎层,在回表之前先在索引记录上过滤,减少回表次数。
- 典型场景:联合索引
(a,b),查询WHERE a=1 AND b LIKE 'x%'——b 不是前缀条件无法用于定位,但 ICP 可以在索引内先过滤 b,避免把所有 a=1 的行都拉回表。 - EXPLAIN 中
Extra = Using index condition即表示 ICP 生效。
-- ICP 生效示例
CREATE TABLE t_user (id BIGINT PRIMARY KEY, name VARCHAR(50), age INT,
KEY idx_name_age (name, age));
EXPLAIN SELECT * FROM t_user WHERE name = '张' AND age > 20;
-- Extra 出现 Using index condition:age>20 在索引内先过滤,再回表
追问
- 问:ICP 和覆盖索引谁更强?答:覆盖索引完全不用回表(Using index);ICP 只是减少回表行数,两者可同时出现。
- 问:ICP 对所有条件都生效吗?答:只对索引内的列生效,索引外的条件不行。
题目 12:为什么建议索引选择性高的列?
考点
- 选择性的定义:distinct 值 / 总行数;
- 低选择性对索引性价比的影响。
参考答案
- 选择性 =
COUNT(DISTINCT 列) / COUNT(*),越接近 1 越好; - 选择性高意味着每个索引值对应的行数少,走索引后回表行数少,索引才有价值;
- 低选择性列(性别、状态位)建索引,命中行占比大,优化器常放弃索引,索引变成纯写入负担;
- 折中手段:用联合索引把低选择性列与高选择性列组合,或对低选择性列加业务条件缩小范围。
-- 计算选择性
SELECT COUNT(DISTINCT username) / COUNT(*) AS selectivity FROM t_user;
-- 接近 1 说明 username 适合建索引
SELECT COUNT(DISTINCT status) / COUNT(*) AS selectivity FROM t_order;
-- 接近 0 说明 status 单独建索引性价比很低
追问
- 问:性别列什么时候建索引?答:当查询固定是"性别=男 且 城市=北京 且 最近登录"这类组合时,作为联合索引的后置列参与即可,别单独建。
- 问:前缀索引能提升选择性吗?答:对长字符串可建
(col(10))前缀索引减少索引体积,代价是可能引入误判与无法覆盖。
题目 13:NULL 值对索引的影响?
考点
- NULL 与索引的兼容性;
- 三个值的逻辑(NULL、TRUE、FALSE)。
参考答案
- 索引可以包含 NULL(普通索引、唯一索引都允许),但要注意:
1. WHERE col IS NULL 在 8.0 可以走索引(优化器支持);
2. 唯一索引允许多个 NULL;
3. 用 = 匹配 NULL 永远不成立,必须用 IS NULL / IS NOT NULL / ISNULL()。
- 设计建议:能用 NOT NULL + DEFAULT 表达语义的列尽量 NOT NULL,避免统计和 JOIN 时被 NULL 语义坑到。
-- NULL 查询必须用 IS NULL
SELECT * FROM t_user WHERE deleted_at IS NULL; -- ✅ 走索引(若建有索引)
SELECT * FROM t_user WHERE deleted_at = NULL; -- ❌ 永远查不到,NULL 不等于任何值
追问
- 问:
NOT IN遇到 NULL 会怎样?答:WHERE id NOT IN (1, NULL)结果为空集(NULL 参与比较结果未知),面试高频陷阱,务必会解释。 - 问:COUNT(col) 和 COUNT(*) 在 NULL 上区别?答:COUNT(*) 统计所有行,COUNT(col) 忽略 NULL 行。
题目 14:冗余索引与重复索引有什么区别?
考点
- 重复索引:完全相同的列组合;
- 冗余索引:能被其他索引覆盖的最左前缀;
- 索引治理思路。
参考答案
- 重复索引:同一列建了完全一样的索引(如建表时主键 + 再建一个唯一同列索引);
- 冗余索引:索引 A 的最左前缀已经包含索引 B 的功能,B 就是冗余的。例如已有
(a,b,c),再建(a)就是冗余;但(a,c)和(b)不算冗余(前导列不同)。 - 危害:写入维护多、占空间、优化器选择困难。
- 治理:用
information_schema.STATISTICS排查同前缀索引,删除冗余;8.0 的sys.schema_redundant_indexes视图可直接查。
-- 找出冗余/重复索引(8.0 自带视图)
SELECT * FROM sys.schema_redundant_indexes WHERE table_schema = 'demo';
-- 也可以手写:查同一表所有索引的列前缀
SELECT table_name, index_name, GROUP_CONCAT(column_name ORDER BY seq_in_index) AS cols
FROM information_schema.STATISTICS
WHERE table_schema = 'demo' GROUP BY table_name, index_name;
追问
- 问:
(a,b)和(b,a)算重复吗?答:不算,列顺序不同,各自服务不同的查询模式。 - 问:删冗余索引要注意什么?答:先确认线上没有查询依赖它(看慢日志/performance_schema),选低峰期删。
题目 15:为什么 B+ 树索引能扛住千万级数据?
考点
- 树高与磁盘 IO 的关系;
- 16KB 页与扇出(fanout)计算。
参考答案
- B+ 树非叶子节点只存键和指针,一页 16KB 能放下大量键值,扇出极大,树高通常只有 3~4 层;
- 粗略估算:8 字节 BIGINT + 6 字节指针,每页约可存 16KB/14B ≈ 1000+ 个键,第二层页数就能覆盖百万级键,第三层覆盖上亿;
- 每次查询沿树定位只做 树高次磁盘 IO(约 3~4 次),加上缓存命中,实际大部分 IO 在内存中完成;
- 千万级数据对 B+ 树来说"不成问题",瓶颈通常在回表和排序,而不是树本身。
-- 查看 InnoDB 页大小(8.0 默认 16K)
SHOW VARIABLES LIKE 'innodb_page_size';
-- 16384 = 16KB
追问
- 问:树高能不能压到 2 层?答:可以,提高页大小或缩短键长度(如前缀索引)能增加扇出,但页变大会增加单次 IO 成本,需权衡。
- 问:1000 万行的表建了索引为什么还是慢?答:大概率不是树查找慢,而是回表次数多、扫描行数多、排序或随机 IO——往这三个方向查。
题目 16:MySQL 怎么决定要不要用索引?
考点
- 优化器与代价模型;
- 统计信息(ANALYZE TABLE);
- Explain 中 rows、type、Extra 的判读。
参考答案
- 优化器基于代价模型(随机 IO 次数、顺序 IO 次数、CPU 成本等)估算各方案成本,选最小的执行计划;
- 估算依赖统计信息(
information_schema.STATISTICS的 cardinality),统计信息过期会导致选错索引,可用ANALYZE TABLE刷新; - 判读执行计划三个关键点:
1. type:system > const > eq_ref > ref > range > index > ALL(从好到坏);
2. key:实际用的索引;rows:预估扫描行数(是估算,不是实际值);
3. Extra:Using index(覆盖)、Using index condition(ICP)、Using filesort(额外排序,需警惕)、Using temporary(临时表)。
- 必要时可用
FORCE INDEX强制指定,但根治还是设计合理的索引。
-- 刷新统计信息后重新看执行计划
ANALYZE TABLE t_order;
EXPLAIN SELECT order_no FROM t_order WHERE status = 1 ORDER BY created_at DESC LIMIT 10;
追问
- 问:rows 等于实际扫描行数吗?答:不等于,它是估算值,真实值要看
EXPLAIN ANALYZE(8.0.18+)输出的 actual rows。 - 问:什么时候该 FORCE INDEX?答:极少用;统计信息长期不准且无法刷新时临时兜底,根本解是修正统计或调整索引。
复习清单
| 题目 | 核心结论 | 易错点 |
|---|---|---|
| 索引本质 | B+ 树有序结构,减扫描 | 别答成"二叉树" |
| 聚簇/非聚簇 | 聚簇存整行,二级索引存主键 | 一张表只能一个聚簇索引 |
| 主键选型 | 自增 BIGINT 最优 | UUID 导致页分裂 |
| 回表/覆盖 | 覆盖索引 Extra=Using index | 与 ICP 的 Using index condition 区分 |
| 最左前缀 | 从第一列连续命中 | 范围列之后失序 |
| 索引代价 | 写慢、占空间、低选择性被弃 | 别答"索引永远更快" |
| NULL | 可入索引,比较用 IS NULL | = NULL 永远不成立 |
| 冗余索引 | 同前缀即冗余 | (a,b) 与 (b,a) 不同 |
练习题
练 1:现有联合索引 (city, age, score),判断以下 SQL 能否走索引、能用到哪几列:WHERE age = 20 AND city = '北京' ORDER BY score。
<details>
<summary>参考答案</summary>
等值条件 city、age 可换序(优化器会重排),都用于定位;score 在索引内,ORDER BY score 无需额外排序(索引顺序天然有序),Extra 大概率 Using index condition,无 filesort。
</details>
练 2:写一条 SQL 用 sys.schema_redundant_indexes 找出 demo 库的冗余索引。
<details>
<summary>参考答案</summary>
SELECT * FROM sys.schema_redundant_indexes WHERE table_schema = 'demo';
</details>
下一篇预告:第 2 章 索引优化与失效场景面试精讲
文章回复
0 条公开回复