《MySQL索引核心分类面试高频考点问答清单》
一、基础概念类(入门必问)
Q1:MySQL索引的本质是什么?核心作用有哪些?
答:
- 本质:数据库表中一列或多列值进行排序的存储结构,通过"空间换时间"将随机IO转化为顺序IO,加速数据操作。
- 核心作用:
- 加速数据检索(SELECT)
- 加速排序(ORDER BY)和分组(GROUP BY)
- 加速表连接(JOIN)
- 通过唯一索引保证数据唯一性
Q2:MySQL常用的索引数据结构有哪些?各自特点是什么?
答:
| 数据结构 | 特点 | 适用场景 |
|----------|------|----------|
| B+树 | 所有数据在叶子节点,非叶子节点仅存索引键和指针;支持范围查询、排序、分页 | MySQL默认,绝大多数场景 |
| 哈希索引 | 仅支持等值查询,不支持范围、排序、模糊查询;查询速度极快 | Memory引擎,纯等值查询 |
| 全文索引 | 基于倒排索引实现 | 大文本字段的全文检索 |
| R树索引 | 用于空间数据类型 | 地理信息查询 |
Q3:为什么MySQL选择B+树作为默认索引结构,而不是B树或红黑树?
答:
- B+树 vs B树:
- B+树所有数据都在叶子节点,非叶子节点更小,相同磁盘页能存储更多索引键,树的高度更低(通常3-4层)
- B+树叶子节点形成双向链表,范围查询和排序效率极高
- B+树查询性能更稳定(所有查询都要走到叶子节点)
- B+树 vs 红黑树:
- 红黑树是二叉树,树的高度随数据量增长而增加(百万级数据高度约20层)
- 红黑树不支持范围查询和排序
- 红黑树磁盘IO次数远高于B+树
二、物理存储分类(核心必问)
Q4:什么是聚簇索引?什么是非聚簇索引?核心区别是什么?
答:
- 聚簇索引:将索引结构与数据行物理存储在一起的索引,叶子节点直接存储完整的数据行。一个表只能有一个聚簇索引。
- 非聚簇索引:索引结构与数据行物理存储分离的索引,叶子节点存储索引键和指向数据行的指针(或主键值)。一个表可以有多个非聚簇索引。
核心区别:
| 对比维度 | 聚簇索引 | 非聚簇索引 |
|----------|----------|------------|
| 数量限制 | 一个表只能有一个 | 一个表可以有多个 |
| 物理存储 | 索引与数据行存储在一起 | 索引与数据行分离存储 |
| 叶子节点内容 | 完整的数据行 | 索引键+指针/主键值 |
| 查询速度 | 极快(无需回表) | 较快(可能需要回表) |
| 插入速度 | 慢(可能导致页分裂) | 快 |
| 更新代价 | 高(更新主键会移动数据) | 低 |
Q5:InnoDB引擎中聚簇索引是如何实现的?如果表没有主键会怎样?
答:
InnoDB中主键索引就是聚簇索引,实现规则:
- 如果表定义了主键,InnoDB会将主键作为聚簇索引的键
- 如果没有定义主键,InnoDB会选择第一个唯一非空索引作为聚簇索引
- 如果既没有主键也没有唯一非空索引,InnoDB会隐式创建一个6字节的ROWID作为聚簇索引
Q6:什么是回表操作?为什么InnoDB的二级索引查询需要回表?
答:
- 回表操作:通过二级索引找到主键值后,再根据主键值到聚簇索引中查找完整数据行的过程。
- 原因:InnoDB的二级索引叶子节点只存储主键值,而不是完整的数据行或数据行的物理地址。因此,当查询需要获取索引中没有的列时,必须通过主键值去聚簇索引中查找完整数据行。
Q7:MyISAM和InnoDB在索引实现上有什么主要区别?
答:
| 对比维度 | InnoDB | MyISAM |
|----------|--------|--------|
| 聚簇索引 | 支持(主键索引就是聚簇索引) | 不支持 |
| 二级索引叶子节点 | 存储主键值 | 存储数据行的物理地址指针 |
| 回表操作 | 需要(通过主键查找聚簇索引) | 不需要(直接通过指针访问数据) |
| 索引与数据关系 | 索引与数据存储在同一个文件(.ibd) | 索引与数据分离存储(.MYI和.MYD) |
三、逻辑功能分类(高频考点)
Q8:主键索引有什么特点?设计主键时应该遵循哪些原则?
答:
特点:
- 唯一性:主键值必须唯一,不允许重复
- 非空性:主键值不允许为NULL
- 自动创建:定义主键时,数据库会自动创建主键索引
- 聚簇特性:InnoDB中主键索引决定了数据的物理存储顺序
设计原则:
- 尽量使用自增整数作为主键(如INT AUTO_INCREMENT)
- 避免使用过长的字符串作为主键
- 避免使用业务字段作为主键(业务字段可能会变化)
- 主键值应保持稳定,不频繁更新
Q9:唯一索引和主键索引有什么区别?
答:
| 对比维度 | 主键索引 | 唯一索引 |
|----------|----------|----------|
| 唯一性 | 强制唯一 | 强制唯一 |
| 非空性 | 强制非空 | 允许有一个NULL值 |
| 数量限制 | 一个表只能有一个 | 一个表可以有多个 |
| 聚簇特性 | InnoDB中是聚簇索引 | 非聚簇索引 |
| 作用 | 标识数据行的唯一性 | 保证列值的唯一性 |
Q10:什么是二级索引?二级索引包含哪些类型?
答:
- 二级索引(辅助索引):除了主键索引之外的所有索引都称为二级索引。
- 包含类型:
- 普通索引(Normal Index):最基本的索引,没有任何限制
- 唯一索引(Unique Index):索引列的值必须唯一,但允许有一个NULL值
- 全文索引(Fulltext Index):用于全文检索
- 空间索引(Spatial Index):用于空间数据类型
四、字段数量分类(核心必问)
Q11:什么是联合索引?联合索引遵循什么原则?
答:
- 联合索引(复合索引):基于两个或多个列创建的索引,索引键由多个列的值按顺序组成。
- 遵循最左前缀原则:联合索引的查询条件必须从索引的最左列开始,才能使用索引。可以跳过中间的列,但不能跳过最左列。
示例:对于联合索引idx_a_b_c(a, b, c)
- 可以使用索引:
WHERE a=1、WHERE a=1 AND b=2、WHERE a=1 AND b=2 AND c=3 - 可以部分使用索引:
WHERE a=1 AND c=3(只使用a列) - 不能使用索引:
WHERE b=2、WHERE c=3、WHERE b=2 AND c=3
Q12:联合索引相比多个单列索引有什么优势?
答:
- 减少索引数量:一个联合索引可以替代多个单列索引,减少索引的维护成本和存储空间
- 提高查询效率:可以在索引中过滤更多的数据,减少回表次数
- 支持覆盖索引:更容易实现覆盖索引,避免回表操作
- 加速排序和分组:如果ORDER BY或GROUP BY的列都包含在联合索引中,可以避免文件排序
Q13:设计联合索引时应该遵循哪些原则?
答:
- 选择性高的列放在前面:选择性=不同值的数量/总行数,选择性越高,过滤效果越好
- 经常一起查询的列放在一起:如果多个列经常同时出现在WHERE子句中,应该创建联合索引
- 长度短的列放在前面:可以减少索引的大小,提高查询效率
- 避免创建过长的联合索引:联合索引的列数不宜超过5个
- 考虑查询的顺序:根据业务查询的特点,合理安排列的顺序
五、特殊优化型索引(高频考点)
Q14:什么是覆盖索引?覆盖索引有什么优势?
答:
- 覆盖索引:如果一个索引包含了查询所需的所有列,那么这个索引就称为覆盖索引。覆盖索引不是一种特殊的索引类型,而是索引的一种使用方式。
- 优势:
- 性能极高:避免了回表操作,只需要一次B+树查找
- 减少IO操作:索引通常比数据行小,可以减少磁盘IO
- 可以加速排序和分组:如果ORDER BY或GROUP BY的列都包含在索引中,可以避免文件排序
示例:
CREATE INDEX idx_name_age ON users(name, age);
-- 可以使用覆盖索引
SELECT name, age FROM users WHERE name = '张三';
-- 不能使用覆盖索引(需要回表获取gender列)
SELECT name, age, gender FROM users WHERE name = '张三';
Q15:什么是前缀索引?什么时候应该使用前缀索引?
答:
- 前缀索引:只对字符串列的前N个字符创建索引,而不是对整个字符串创建索引。
- 适用场景:
- 字符串列较长(如VARCHAR(255))
- 字符串的前N个字符已经具有足够的选择性
- 对索引大小敏感的场景
创建方法:
-- 对email列的前10个字符创建索引
CREATE INDEX idx_email_prefix ON users(email(10));
Q16:如何选择合适的前缀长度?前缀索引有什么限制?
答:
选择前缀长度的方法:
计算不同前缀长度的选择性,选择选择性接近整个字符串选择性的最小前缀长度:
-- 计算整个字符串的选择性
SELECT COUNT(DISTINCT email)/COUNT(*) FROM users;
-- 计算不同前缀长度的选择性
SELECT
COUNT(DISTINCT LEFT(email, 5))/COUNT(*) AS selectivity_5,
COUNT(DISTINCT LEFT(email, 10))/COUNT(*) AS selectivity_10,
COUNT(DISTINCT LEFT(email, 15))/COUNT(*) AS selectivity_15
FROM users;
限制:
- 不支持ORDER BY和GROUP BY
- 不支持覆盖索引(因为索引中没有存储完整的字符串)
- 不能用于LIKE '%xxx'查询
六、概念辨析类(易混淆考点)
Q17:主键索引和聚簇索引是什么关系?
答:
- 在InnoDB中,主键索引就是聚簇索引
- 在MyISAM中,主键索引是非聚簇索引
- 聚簇索引是物理存储方式,主键索引是逻辑功能
- 一个表只能有一个聚簇索引,但可以有多个唯一索引
Q18:二级索引和非聚簇索引是什么关系?
答:
- 在InnoDB中,所有二级索引都是非聚簇索引
- 非聚簇索引是物理存储方式,二级索引是逻辑功能
- 一个表可以有多个二级索引和多个非聚簇索引
Q19:联合索引和覆盖索引是什么关系?
答:
- 联合索引是按字段数量分类的索引类型
- 覆盖索引是索引的一种使用方式
- 联合索引更容易实现覆盖索引,因为它包含多个列
- 单列索引也可以成为覆盖索引(如果查询只需要该列)
七、设计与使用最佳实践(高频考点)
Q20:索引设计应该遵循哪些基本原则?
答:
- 优先考虑联合索引:一个联合索引可以替代多个单列索引,减少索引数量
- 覆盖索引优先:尽量让索引包含查询所需的所有列,避免回表操作
- 选择性高的列优先:选择性越高,索引的过滤效果越好
- 避免创建过多索引:过多的索引会降低插入、更新和删除的性能
- 避免在低选择性列上创建索引:如性别、状态等只有少数几个值的列
- 避免在频繁更新的列上创建索引:更新索引的代价很高
- 使用自增整数作为主键:避免页分裂,提高插入性能
Q21:列举常见的索引失效场景
答:
- 在索引列上使用函数或表达式:
WHERE YEAR(create_time) = 2023 - 在索引列上进行计算:
WHERE age + 1 = 20 - 使用!=或<>操作符:
WHERE status!= 1 - 使用IS NULL或IS NOT NULL:
WHERE name IS NULL - 使用LIKE '%xxx'查询:
WHERE name LIKE '%张三' - 字符串不加引号:
WHERE phone = 13800138000(phone是VARCHAR类型) - 联合索引不满足最左前缀原则:
WHERE b=2 AND c=3(索引是idx_a_b_c) - 使用OR连接多个条件:
WHERE a=1 OR b=2(b列没有索引) - 隐式类型转换:如VARCHAR类型的列与数字比较
Q22:如何判断一个索引是否有效?如何优化低效的索引?
答:
判断索引是否有效:
- 使用EXPLAIN分析执行计划,查看type、key、rows、Extra等字段
- 查看索引的使用情况:
SHOW INDEX FROM table_name; - 查看慢查询日志,找出没有使用索引的查询
优化低效的索引:
- 删除无用的索引:
DROP INDEX index_name ON table_name; - 优化联合索引的列顺序,将选择性高的列放在前面
- 增加覆盖索引,避免回表操作
- 对于长字符串列,使用前缀索引
- 定期分析表和索引:
ANALYZE TABLE table_name;
八、进阶考点(大厂高频)
Q23:为什么InnoDB不建议使用UUID作为主键?
答:
- 插入性能差:UUID是无序的,插入数据时会导致聚簇索引频繁页分裂,产生大量碎片
- 索引体积大:UUID是128位的,比自增整数大很多,导致索引体积增大,查询效率降低
- 缓存命中率低:无序的主键导致数据在磁盘上分散存储,缓存命中率低
Q24:什么是索引下推(ICP)?它是如何优化查询性能的?
答:
- 索引下推(Index Condition Pushdown):MySQL 5.6引入的优化特性,允许在存储引擎层使用索引中的列进行条件过滤,而不是将所有满足索引前缀条件的数据都返回给MySQL服务层进行过滤。
- 优化原理:
- 没有ICP时:存储引擎根据索引前缀找到数据,返回给服务层,服务层再根据其他条件过滤
- 有ICP时:存储引擎在遍历索引时,直接使用索引中的列进行条件过滤,只返回满足条件的数据给服务层
- 优势:减少了回表次数和数据传输量,提高了查询性能
示例:
对于联合索引idx_name_age(name, age),查询SELECT * FROM users WHERE name LIKE '张%' AND age = 20;
- 没有ICP时:找到所有name以'张'开头的数据,回表获取完整数据,再过滤age=20的记录
- 有ICP时:在索引中直接过滤age=20的记录,只回表获取满足条件的数据
Q25:什么是MRR(Multi-Range Read)优化?它解决了什么问题?
答:
- MRR(多范围读):MySQL 5.6引入的优化特性,用于优化二级索引的范围查询。
- 解决的问题:二级索引范围查询时,回表操作是随机IO,性能较差。
- 优化原理:
- 先将二级索引查询得到的主键值排序
- 然后按照主键的顺序进行回表操作
- 将随机IO转化为顺序IO,提高了查询性能
《一页纸速记版》
一、基础概念速记
- 本质:排序的存储结构,空间换时间,随机IO→顺序IO
- 核心作用:加速查询、排序、分组、JOIN,保证唯一性
- 默认数据结构:B+树(所有数据在叶子节点,双向链表,范围查询快)
二、物理存储分类(核心必背)
| 类型 | 核心特点 | 数量 | 叶子节点内容 | 查询代价 |
|---|---|---|---|---|
| 聚簇索引 | 索引与数据物理在一起 | 1个/表 | 完整数据行 | 无需回表,极快 |
| 非聚簇索引 | 索引与数据分离 | 多个/表 | 索引键+主键值(InnoDB) | 可能需要回表 |
- InnoDB聚簇索引规则:主键→第一个唯一非空索引→隐式6字节ROWID
- 回表:二级索引找主键→聚簇索引找完整数据,两次B+树查找
三、逻辑功能分类
- 主键索引:唯一+非空,InnoDB中就是聚簇索引
- 唯一索引:唯一,允许1个NULL,非聚簇
- 二级索引:除主键外的所有索引,都是非聚簇索引
四、字段数量分类
- 联合索引:多列组成,遵循最左前缀原则(必须从最左列开始)
- 联合索引优势:减少索引数量、提高过滤效率、易实现覆盖索引
- 设计原则:选择性高→经常一起查询→长度短的列放前面
五、特殊优化型索引
- 覆盖索引:包含查询所有列,无需回表,性能最高
- 前缀索引:字符串前N个字符建索引,减少空间
- 前缀长度选择:选择性接近完整字符串的最小长度
六、核心概念辨析
- 主键索引=聚簇索引(InnoDB)
- 二级索引=非聚簇索引(InnoDB)
- 覆盖索引是使用方式,不是索引类型
- 联合索引是索引类型,易实现覆盖索引
七、索引失效场景(必考)
- 索引列上用函数/计算
- 使用!=、<>、IS NULL/IS NOT NULL
- LIKE '%xxx'(左模糊)
- 字符串不加引号(隐式类型转换)
- 联合索引不满足最左前缀
- OR连接无索引的列
八、设计最佳实践
- 用自增整数做主键,避免UUID
- 优先联合索引,避免过多单列索引
- 优先覆盖索引,减少回表
- 不在低选择性列(性别、状态)建索引
- 不在频繁更新的列建索引
九、进阶优化
- 索引下推(ICP):存储引擎层用索引列过滤,减少回表
- MRR:主键排序后回表,随机IO→顺序IO