MySQL 高性能分页查询实战指南(含 Explain 分析 + 经典写法)

举报
柠檬味拥抱 发表于 2025/04/24 09:43:35 2025/04/24
【摘要】 MySQL 高性能分页查询实战指南(含 Explain 分析 + 经典写法)面试官:你怎么优化分页查询?你:覆盖索引 + 子查询定位 + 延迟关联,一套组合拳,干就完了! 一、覆盖索引(Covering Index) ✅ 什么是覆盖索引?当一个索引包含了查询所需的所有字段,不需要回表查聚簇索引的数据,称为“覆盖索引”。比如:-- 假设 email 是普通索引SELECT email FRO...

MySQL 高性能分页查询实战指南(含 Explain 分析 + 经典写法)

面试官:你怎么优化分页查询?
你:覆盖索引 + 子查询定位 + 延迟关联,一套组合拳,干就完了!


一、覆盖索引(Covering Index)

✅ 什么是覆盖索引?

当一个索引包含了查询所需的所有字段,不需要回表查聚簇索引的数据,称为“覆盖索引”。

比如:

-- 假设 email 是普通索引
SELECT email FROM users WHERE email = 'abc@example.com';

上面查询只用 email 字段,不需要访问表内其他数据,直接在索引上就能拿到结果 → 避免回表


✅ 如何实现?

创建联合索引,将查询字段和 WHERE 条件字段都放进去。

例:

-- 假设业务频繁用 email 查询 name
CREATE INDEX idx_email_name ON users(email, name);

-- 然后执行:
SELECT name FROM users WHERE email = 'abc@example.com';

📌 查询字段 name 与过滤字段 email 都在联合索引中,查询时可完全在索引中完成,无需回表。


✅ 如何验证是否生效?

使用 EXPLAIN 查看 Extra 字段是否有:

Using index   ✅ --> 覆盖索引
Using where   ❌ --> 说明还要过滤
Using temporary, filesort ❌ --> 说明没有用到索引

二、超大分页优化(比如 page = 10000)

🧨 问题背景

常规分页:

SELECT * FROM articles ORDER BY id LIMIT 10000, 10;

会跳过前 10000 条 → 性能极差(全表扫描 + 数据丢弃)


✅ 优化方案一:子查询定位 + 延迟关联(推荐)

-- 第一步:先查出目标页的起始ID
SELECT id FROM articles ORDER BY id LIMIT 10000, 10;

-- 第二步:再回表查整行数据
SELECT * FROM articles WHERE id IN (
  结果中的10个id
);

📌 优点:

  • 第一步在索引上完成,速度快
  • 第二步只查10条,不会有全表扫描

✅ 优化方案二:使用“游标式”分页(适合大表)

-- 从上一页最后一条记录开始
SELECT * FROM articles WHERE id > 上一页最后的ID ORDER BY id LIMIT 10;

📌 原理:利用 索引的顺序性,比 offset 更高效。


✅ 优化方案三:只查 ID + join 查数据(延迟加载)

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

📌 这是子查询 + join 延迟关联的一种写法,效率也很高。


三、常见面试高频问答

Q:什么是覆盖索引?如何避免回表?
A:查询字段都在索引中时,叫覆盖索引,不需要回表。设计联合索引时将查询字段一起放入即可。

Q:分页查10000页很慢怎么办?
A:使用 ID 游标法(WHERE id > ?)或子查询 + 延迟关联,减少数据跳过和回表次数。


四、总结一句话 🚀

覆盖索引是减少 IO 的利器,超大分页优化是高性能接口的必备技能!


image-20250421161543919

image-20250421161556249

📚 索引创建的8条黄金原则(MySQL为例)

✅ 原则一:最左前缀原则

联合索引中遵循“从左到右”的顺序使用

CREATE INDEX idx_name_age ON users(name, age);
  • WHERE name = '张三' → 命中索引
  • WHERE name = '张三' AND age = 18 → 命中索引
  • WHERE age = 18索引失效

💡 联合索引本质上是一个排序的 B+ 树结构,只有从最左字段起连续使用才会触发索引。


✅ 原则二:选择性高的列建索引

选择性 = 唯一值 / 总行数,越接近1越合适建索引

例子:

  • 手机号:高选择性,适合建索引 ✅
  • 性别:选择性低,索引作用不大 ❌

✅ 原则三:频繁出现在 WHERE、JOIN、ORDER BY、GROUP BY 的字段优先建索引

SELECT * FROM orders WHERE user_id = 123;  -- ✅
SELECT * FROM orders ORDER BY created_at DESC;  -- ✅

它们都会触发全表扫描排序临时表,用索引能有效避免。


✅ 原则四:避免对索引字段进行函数或表达式操作

-- ❌ 会导致索引失效
WHERE DATE(created_at) = '2024-01-01'
WHERE price + 10 > 100

-- ✅ 改写方式
WHERE created_at BETWEEN '2024-01-01' AND '2024-01-02'
WHERE price > 90

✅ 原则五:避免在索引字段上使用不等于(<>、!=)、LIKE ‘%xxx’ 等模糊匹配

-- ❌ LIKE '%abc' 会导致索引失效
-- ✅ LIKE 'abc%' 可以使用索引

✅ 原则六:小表不需要建索引,频繁更新的字段慎用索引

  • 小表全表扫描比建索引更快(例如字典表)
  • 经常更新的字段如果建立索引,会有大量写开销

✅ 原则七:覆盖索引优先,减少回表

把查询字段都包含在索引中,避免回表开销

-- 索引包含所有查询字段
CREATE INDEX idx_user_email ON users(name, email);

SELECT email FROM users WHERE name = '张三';  -- ✅ 覆盖索引

✅ 原则八:控制索引数量,适度而非越多越好

  • 每增加一个索引,写入时多维护一棵 B+ 树
  • 会增加 INSERT / UPDATE / DELETE 的性能开销
  • 推荐控制在3~5个有效索引,做定期审查

🧠 面试官常问:

Q:如何判断哪些字段需要建索引?

答:结合实际查询频率、慢 SQL 分析、字段选择性、排序分组操作、回表判断等综合考量。

Q:如何知道索引是否生效?

答:用 EXPLAINtype 是否为 refrangeExtra 中是否有 Using index

✅ 总结:建索引不是越多越好,遵循“高频、高选择性、参与条件判断”的标准,适配业务才是王道!

如果你需要我帮你写一套“索引优化实战案例 + explain解析 + 图解B+树结构”,我可以继续安排成系列文章📘。需要的话跟我说一声~

image-20250421163011631

image-20250421163044410

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。