索引能让查询变快,但索引建错了、用错了,反而会拖慢写入,还可能让查询根本用不上。真正实战时,最重要的不是背“索引有哪些”,而是能判断:什么时候该建索引、怎么建、为什么失效、深分页为什么慢。

在这里插入图片描述

什么时候适合建索引

建索引前先问三个问题:

  1. 表数据量是否足够大?
  2. 这个字段是否经常用于查询、排序、分组?
  3. 字段区分度是否足够高?

如果一张表只有几百行,索引收益通常不明显。如果一个字段只有“男/女”或“启用/禁用”几种值,单独建索引也不一定划算。

常见原则如下:

原则 说明
大表高频查询字段优先 数据量越大,索引收益越明显
WHERE、ORDER BY、GROUP BY 字段优先 这些字段直接影响过滤和排序成本
选择区分度高的列 区分度越高,扫描范围越小
长字符串考虑前缀索引 降低索引体积
优先考虑联合索引 兼顾过滤、排序和覆盖索引
控制索引数量 索引会增加写入和维护成本
索引列尽量 NOT NULL 便于优化器判断和使用索引

联合索引与最左前缀法则

假设有联合索引:

CREATE INDEX idx_name_status_address
ON tb_seller(name, status, address);

它的顺序是 name -> status -> address。查询必须从最左列开始匹配,不能跳过中间列。

查询条件 是否充分使用索引
where name = ? 可以使用 name
where name = ? and status = ? 可以使用 name,status
where name = ? and status = ? and address = ? 可以使用三列
where status = ? 不满足最左前缀
where name = ? and address = ? status 被跳过,后续列利用受限

最左前缀不是死记硬背,它来自 B+ 树联合排序的规则。联合索引先按第一列排序,第一列相同再按第二列排序,第二列相同再按第三列排序。

索引失效的常见场景

在这里插入图片描述

判断一条 SQL 为什么没用好索引,可以先按下面这棵树走一遍:

SQL 没有按预期使用索引

是否是联合索引

是否满足最左前缀

调整查询条件或索引字段顺序

中间是否出现范围查询

范围右侧列可能无法继续精确使用

索引列是否被函数或表达式包住

改写 SQL 或增加可索引字段

是否发生隐式类型转换

修正字段类型和参数写法

like 是否以百分号开头

普通 B+ 树无法定位起点

继续看数据分布和优化器成本选择

1. 违反最左前缀法则

联合索引没有从最左列开始使用,或者中间跳过某列,会导致索引无法完整使用。

2. 范围查询右侧列难以继续使用

WHERE name = 'Tom'
  AND status > 1
  AND address = 'Shanghai'

如果 status 是范围查询,那么它右边的 address 通常难以继续利用索引进行精确定位。

3. 在索引列上做运算或函数

WHERE substring(phone, 1, 3) = '138'

这类写法会让优化器难以直接利用原始索引值。更好的方式是改写查询,或增加额外字段保存可索引的结果。

4. 字符串不加引号

WHERE phone = 13800138000

如果 phone 是字符串类型,MySQL 可能发生隐式类型转换,导致索引使用异常。应该写成:

WHERE phone = '13800138000'

5. like 以百分号开头

WHERE name LIKE '%Tom'

B+ 树索引适合从左到右匹配。头部模糊时,无法确定从索引哪个位置开始查。

尾部模糊通常更友好:

WHERE name LIKE 'Tom%'

用 EXPLAIN 快速验证

判断索引是否生效,别靠感觉,直接看执行计划:

EXPLAIN
SELECT id, name
FROM tb_seller
WHERE name = 'Tom' AND status = 1;

重点观察:

字段 怎么看
key 实际使用的索引
key_len 联合索引用到了多少
type 是否出现 allindex
rows 预估扫描行数是否过大
Extra 是否出现额外排序、临时表、覆盖索引等信息

深分页为什么慢

普通分页常这样写:

SELECT *
FROM tb_sku
ORDER BY id
LIMIT 9000000, 10;

它并不是直接跳到第 9000000 行再取 10 条,而是要先找到并排序前 9000010 条记录,再丢弃前 9000000 条。页码越深,丢弃成本越高。

覆盖索引加子查询优化

一种常见优化思路是先用覆盖索引查出目标页的主键,再回表取完整数据。

SELECT t.*
FROM tb_sku t
JOIN (
  SELECT id
  FROM tb_sku
  ORDER BY id
  LIMIT 9000000, 10
) a ON t.id = a.id;

这个写法的关键是:子查询只扫描主键或覆盖索引,减少排序和回表的数据量。

在这里插入图片描述

深分页优化的关键,是先让 MySQL 少搬数据:

请求第 900000 页

直接 limit offset?

扫描大量记录并丢弃

排序和回表成本很高

子查询只查 id

利用主键或覆盖索引完成分页

再按 id 回表取 10 条完整数据

返回当前页

如果业务允许,也可以改成游标分页:

SELECT id, title, price
FROM tb_sku
WHERE id > ?
ORDER BY id
LIMIT 10;

游标分页不适合任意跳页,但非常适合信息流、列表向下加载等场景。

面试回答模板

可以这样回答:

索引设计要结合数据量、查询频率、字段区分度和排序分组场景。常用作 where、order by、group by 的高区分度字段适合建索引,长字符串可以考虑前缀索引,高频组合查询可以建联合索引并尽量覆盖查询字段。索引失效常见原因有违反最左前缀、范围查询右侧列无法继续使用、在索引列上做函数或运算、字符串不加引号、like 以百分号开头。深分页慢是因为 MySQL 需要扫描并丢弃大量前置记录,可以用覆盖索引加子查询,或者改成基于游标的分页。

小结

索引优化的核心是“让 MySQL 少扫描、少排序、少回表”。建索引前先看业务查询,写 SQL 时避免破坏索引使用,遇到深分页时减少无效扫描。最后一定用 EXPLAIN 验证,而不是凭感觉判断。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐