java面试必问14:MySQL 索引类型:从基础到优化,面试官给你点赞

MySQL 索引类型:从基础到优化,一篇讲透

面试官:"MySQL 有哪些索引类型?"

你:"主键索引、唯一索引、普通索引、复合索引、全文索引。索引能大大加快查询速度,但会降低增删改的性能。"

面试官:"那复合索引的最左前缀原则是什么?为什么会有这个原则?"

你:"......"

很多人能列出索引类型,但一追问"什么时候索引会失效""如何设计高效索引"就含糊了。本文从索引的底层结构出发,讲透各种索引的特点和使用场景。


一、索引是什么?

索引是数据库中的一种数据结构 ,类似于书的目录,可以快速定位数据所在的位置,避免全表扫描。MySQL 中常用的索引结构是 B+Tree(InnoDB 存储引擎)。索引虽然能加速查询,但需要额外的存储空间,并且在数据插入、更新、删除时需要同步维护,因此会降低写入性能。


二、五种索引类型详解

1. 主键索引(PRIMARY KEY)

  • 特点:唯一且非空,一个表只能有一个主键索引。
  • 底层:聚簇索引(Clustered Index),叶子节点直接存储整行数据。
  • 创建:
sql 复制代码
CREATE TABLE user (
    id INT PRIMARY KEY,   -- 主键索引
    name VARCHAR(50)
);
-- 或单独添加
ALTER TABLE user ADD PRIMARY KEY (id);
  • 注意:InnoDB 中,如果没有显式定义主键,会自动选择一个唯一非空索引作为聚簇索引;如果也没有,则隐式生成一个 6 字节的 rowid 作为主键。建议每个表都显式指定主键。

2. 唯一索引(UNIQUE)

  • 特点:索引列的值必须唯一,但允许 NULL(可以有多个 NULL)。
  • 作用:保证数据唯一性,同时加速查询。
  • 创建:
sql 复制代码
CREATE TABLE user (
    id INT PRIMARY KEY,
    email VARCHAR(100) UNIQUE   -- 唯一索引
);
-- 或
CREATE UNIQUE INDEX idx_email ON user(email);

3. 普通索引(INDEX / KEY)

  • 特点:最基本索引,无唯一性约束,允许重复值。
  • 作用:加速查询,是最常用的索引类型。
  • 创建:
sql 复制代码
CREATE INDEX idx_name ON user(name);

4. 复合索引(Composite Index)

  • 特点 :在多个列上建立一个索引,遵循最左前缀原则。
  • 作用 :同时加速对多个列的查询,尤其适合 WHERE 条件中包含多个字段的场景。
  • 创建:
sql 复制代码
CREATE INDEX idx_name_age ON user(name, age);

查询 WHERE name = '张三' AND age = 25 可以利用该索引;但 WHERE age = 25 不能使用该索引(因为没有使用索引的最左列 name)。

5. 全文索引(FULLTEXT)

  • 特点 :用于全文搜索(类似搜索引擎),仅支持 MyISAM 和 InnoDB(MySQL 5.6+)。
  • 作用 :对文本内容进行关键词匹配,比 LIKE '%keyword%' 高效得多。
  • 创建:
sql 复制代码
CREATE TABLE article (
    id INT PRIMARY KEY,
    content TEXT,
    FULLTEXT(content)
);
-- 查询
SELECT * FROM article WHERE MATCH(content) AGAINST('数据库优化');
  • 注意:全文索引有停用词、最小词长度等限制,适合大文本字段的搜索场景。

三、索引的优缺点

优点 缺点
大大加快 SELECT 查询速度 占用额外磁盘空间
加速 ORDER BY、GROUP BY 降低 INSERT、UPDATE、DELETE 速度(需维护索引)
唯一索引能保证数据唯一性 索引设计不合理会导致查询优化器选错索引

权衡:索引不是越多越好。一般建议对查询频繁、区分度高的列建索引,避免在低基数列(如性别)或频繁更新的列上建索引。


四、复合索引的最左前缀原则(重点)

复合索引 (a, b, c) 实际上相当于创建了三个索引:(a)、(a, b)、(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,c 无法使用,但 a 可过滤一部分)

不能用索引的情况:

  • WHERE b = 2(没有最左列 a)
  • WHERE b = 2 AND c = 3

范围查询的影响:

  • WHERE a = 1 AND b > 2 AND c = 3:a 和 b 能用到索引,c 用不到(因为 b 是范围查询,后面的列停止匹配)。

设计建议:将区分度高的列放在复合索引左侧,等值查询的列优先于范围查询的列。


五、索引失效的常见场景

场景 示例 原因
对索引列使用函数 WHERE YEAR(create_time) = 2024 无法使用索引,应改为范围查询
隐式类型转换 WHERE phone = 13800138000(phone 是 varchar) 类型不匹配,索引失效
LIKE 以通配符开头 WHERE name LIKE '%张三' 无法使用索引
OR 前后未全索引 WHERE a = 1 OR b = 2(只有 a 有索引) 需要全表扫描
使用 != 或 <> WHERE status != 0 非等值查询一般不用索引
使用 IS NULL 或 IS NOT NULL 某些情况下失效,取决于版本和数据分布 -

六、如何评估索引是否有效?

使用 EXPLAIN 命令查看执行计划:

sql 复制代码
EXPLAIN SELECT * FROM user WHERE name = '张三';

关键列:

  • type :const > ref > range > index > ALL(好到差)
  • possible_keys:可能使用的索引
  • key:实际使用的索引
  • rows:预估扫描行数
  • Extra :Using index 表示覆盖索引(不回表),Using where 表示需要过滤

七、常见面试追问

Q1:主键索引和唯一索引的区别?

  • 主键索引不允许 NULL,唯一索引允许 NULL(多个 NULL)。
  • 一个表只能有一个主键,可以有多个唯一索引。
  • 主键通常是聚簇索引(InnoDB),唯一索引是辅助索引。

Q2:为什么推荐使用自增整数做主键?

  • 插入时顺序写入,B+Tree 页分裂少,性能高。
  • UUID 或随机字符串作为主键会导致随机插入,页分裂频繁,且占用空间大。

Q3:什么是覆盖索引?

如果一个索引包含了查询所需的所有列(即 SELECT 的列都在索引中),那么不需要回表查询数据行,称为覆盖索引。例如:

sql 复制代码
CREATE INDEX idx_name ON user(name);
SELECT name FROM user WHERE name = '张三';  -- 覆盖索引,不回表

Q4:索引下推(Index Condition Pushdown)是什么?

MySQL 5.6 引入的优化:在索引遍历过程中,对索引中包含的字段先做条件过滤,减少回表次数。例如 INDEX(a, b),查询 WHERE a = 1 AND b = 2,没有 ICP 时会先根据 a=1 回表再过滤 b;有 ICP 时直接在索引中判断 b=2,只回表符合的数据。

Q5:联合索引中,字段顺序如何设计?

  • 区分度高的列放在左侧。
  • 等值查询的列放在左侧,范围查询的列放在右侧。
  • 经常用于排序的列可以考虑加入索引(ORDER BY 也能利用索引顺序)。

八、总结

索引类型 特点 适用场景
主键索引 唯一、非空、聚簇 每张表必备,作为行标识
唯一索引 值唯一,可 NULL 保证字段唯一性(如邮箱、手机号)
普通索引 无约束 加速查询,最常用
复合索引 多列组合,最左前缀 多条件查询、排序
全文索引 关键词匹配 大文本搜索(如文章、评论)

一句话记住索引设计 :等值左前缀,范围右靠后;覆盖索引少回表,函数运算全失效。

索引是把双刃剑,合理的索引能让查询飞起来,滥用索引会让写入慢如牛。理解索引类型和原理,是 MySQL 优化的第一步。

希望这篇文章能帮你彻底掌握 MySQL 索引的相关知识,从容应对面试和实际调优,欢迎继续讨论。

我的个人简介最后有一段内容,感兴趣的朋友可以去找找看。那里有我日常分享的技术深度 解析和职场避坑指南,期待与您继续交流。

相关推荐
丑陋小蚊子1 小时前
项目数据不能公开,就写清交付物
面试·求职招聘·大学生·简历·简历下载
Wang's Blog5 小时前
Java 项目实战: 外卖平台优化-Nginx配置文件结构与块层级
java·开发语言·nginx
2601_949950635 小时前
练题簿在线免费刷题 从资料整理到考前自测
面试·职场和发展·pdf·word·刷题
\光辉岁月/6 小时前
5.java-数组
java·开发语言
小马同学-6 小时前
MySQL主从复制和读写分离
数据库·mysql
谢亮_vipxieliang6 小时前
Spring 事务失效的常见场景
java·开发语言·数据库·spring boot
时间的拾荒人7 小时前
Qt 多线程详解:从 QThread 到实战
开发语言·qt·面试
海绵宝宝转agent7 小时前
MySql高频面试八股开源笔记总结
mysql·面试·开源
郑州光合科技余经理7 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
2601_962071577 小时前
类变量和全局变量的生命周期有什么区别?
java·开发语言·jvm