当前位置: 首页 > news >正文

MySQL索引优化:让查询快如闪电

以下内容主要来自 MySQL 官方文档中关于 索引优化、ORDER BY 与 GROUP BY 的执行方式 的部分。这些内容涉及数据库查询优化器如何利用索引来避免排序(filesort)和减少临时表的使用,从而提升查询性能。

下面我将用通俗易懂的方式为你系统地解释这些核心概念,并帮助你理解文档中的每一个要点。


一、核心思想:让索引代替排序

在 MySQL 中,如果一个查询包含 ORDER BYGROUP BY,数据库通常需要对结果进行排序或分组。如果没有合适的索引,MySQL 就必须:

  1. 先查出所有匹配的数据行;
  2. 把它们放入内存或磁盘上的临时表;
  3. 再进行排序或分组 —— 这个过程叫做 filesort(文件排序),非常耗资源。

但如果有一个合适的 有序索引(如 BTREE 索引),并且查询结构能“顺着”这个索引走,那么就可以:

直接按顺序读取数据,跳过排序步骤!

这就是整个文档的核心目标:通过合理设计索引和 SQL 写法,让 MySQL 利用索引的“天然顺序”,避免额外排序和临时表。


二、什么时候可以用索引避免 ORDER BY 排序?

✅ 情况1:ORDER BY 字段是索引的最左前缀,且方向一致

-- 假设索引是 (key_part1, key_part2)
SELECT * FROM t1 ORDER BY key_part1, key_part2;

✔️ 可以利用索引顺序,无需排序。

因为 BTREE 索引本身就是先按 key_part1 排,再按 key_part2 排,所以遍历索引就是有序的。


✅ 情况2:WHERE 条件固定了前面字段,ORDER BY 后面字段

-- 索引仍是 (key_part1, key_part2)
SELECT * FROM t1 
WHERE key_part1 = 'const' 
ORDER BY key_part2;

✔️ 能用索引!

为什么?
因为 key_part1 = const 相当于锁定了索引树的一个子集(比如只看 key_part1='A' 的那一块),这一块内部已经按照 key_part1, key_part2 排好序了,而 key_part1 是常量,所以剩下的自然就是按 key_part2 排好的。

👉 相当于在一个“二级目录”里,内容已经是排好序的。


✅ 情况3:多个字段 DESC 排序,但索引也是 DESC 或可反向扫描

SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;

✔️ 如果索引是 (key_part1 ASC, key_part2 ASC),也能用!
→ 因为 MySQL 支持 反向扫描索引(backward index scan)。

更进一步:

SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;

✔️ 也可以用索引,只要索引定义支持混合方向(MySQL 8.0+ 支持 降序索引):

CREATE INDEX idx ON t1(key_part1 DESC, key_part2 ASC);

📌 关键点:索引的方向要和 ORDER BY 匹配,或者可以通过反向扫描模拟出来。


✅ 情况4:范围查询 + ORDER BY 主键本身

SELECT * FROM t1 WHERE key_part1 > 10 ORDER BY key_part1 ASC;

✔️ 可以用索引 (key_part1, ...),因为:

  • 先找到 >10 的第一个位置,
  • 然后顺着索引往后读,每一条都满足条件且自动有序。

同理:

SELECT * FROM t1 WHERE key_part1 < 10 ORDER BY key_part1 DESC;

✔️ 也可以用索引,只是从右往左扫描。


✅ 情况5:复合条件 + ORDER BY 后续字段

SELECT * FROM t1 
WHERE key_part1 = 'c1' AND key_part2 > 'c2' 
ORDER BY key_part2;

✔️ 依然可以利用索引 (key_part1, key_part2)

原因还是:key_part1 固定 → 锁定一个范围;在这个范围内,key_part2 是有序的。


❌ 哪些情况不能用索引排序?(即必须 filesort)

以下是文档列出的典型 无法使用索引排序 的场景:

场景示例原因
使用不同索引的列排序ORDER BY key1, key2 (key1 和 key2 属于不同索引)索引之间无顺序关系
非连续使用复合索引WHERE key2=const ORDER BY key1_part1, key1_part3跳过了中间列,破坏了最左前缀原则
排序列不在同一个索引中WHERE key2=const ORDER BY key1扫描的是 key2 索引,但排序要用 key1,不匹配
对表达式排序ORDER BY ABS(key), ORDER BY -key表达式改变了原始值,索引存的是原值
多表 JOIN,ORDER BY 不在驱动表上JOIN 多张表,ORDER BY 的列不在第一个被扫描的非 const 表上数据是拼接出来的,顺序混乱
ORDER BY 和 GROUP BY 不一致GROUP BY a; ORDER BY b分组和排序字段不同

三、GROUP BY 如何利用索引?

类似 ORDER BYGROUP BY 也需要对数据分组。传统做法是:

  • 查出所有行 → 放进临时表 → 哈希分组 or 排序分组。

但如果索引设计得好,可以直接利用索引的有序性来“跳着读”,实现高效分组。

有两种主要方式:

1. Loose Index Scan(松散索引扫描)

💡 思想:每个组只读一条记录即可确定该组的存在,不需要遍历所有行。

适用条件:

  • 查询只涉及一张表;
  • GROUP BY 的列构成索引的 最左前缀
  • 没有聚合函数依赖于未参与分组的列;
  • 最重要:索引是有序的(BTREE)。

🌰 例子:

-- 索引: (a, b, c)
SELECT a, b, COUNT(*) FROM t1 GROUP BY a, b;

✔️ 可以使用 Loose Index Scan:

  • 直接沿着索引走,遇到新的 (a,b) 组合就开启新组;
  • 不需要加载所有行到内存。

⚠️ 不适用的情况:

GROUP BY b, c;     -- 不是最左前缀
GROUP BY a, d;     -- d 不在索引中

注意:Loose Index Scan 在 MySQL 5.7+ 中受限较多,8.0 引入了 Skip Scan 后有所改善。


2. Tight Index Scan(紧密索引扫描)

💡 思想:先做一个范围扫描(range scan),取出所有符合条件的行,然后对这些行做排序或哈希分组。

虽然不如 Loose 快,但比全表扫描+临时表好。

适用于:

  • 有 WHERE 条件限制;
  • GROUP BY 列仍是索引前缀;
  • 但条件导致无法“跳跃”访问。

四、Descending Indexes(降序索引)—— MySQL 8.0 新特性

以前的索引都是升序(ASC)存储,即使写 ORDER BY col DESC,也只能通过 反向扫描 实现。

但在某些复杂排序中,比如:

ORDER BY col1 DESC, col2 ASC

旧版本可能无法有效利用索引。

从 MySQL 8.0 开始,支持创建 真正的降序索引

CREATE INDEX idx ON t1(col1 DESC, col2 ASC);

这样就能完美匹配混合排序需求,极大提高这类查询的性能。


五、总结:如何写出能用索引优化的 ORDER BY / GROUP BY?

技巧说明
✅ 使用最左前缀ORDER BY / GROUP BY 的列应构成索引的最左开始部分
✅ 让 WHERE 固定前导列col1 = const,可以让后续列直接利用索引顺序
✅ 注意排序方向尽量让 ORDER BY 方向与索引一致,或使用降序索引
✅ 单一索引覆盖所有排序/分组字段应在同一个 BTREE 索引中
✅ 避免表达式ORDER BY -colABS(col) 等会破坏索引使用
✅ 合理设计复合索引根据常用查询模式建立 (a,b)(a,b,c) 类型索引
✅ 升级到 MySQL 8.0+支持降序索引、Skip Scan,大幅提升灵活性

六、实战建议

假设你有一张订单表:

CREATE TABLE orders (user_id INT,create_time DATETIME,amount DECIMAL(10,2),status TINYINT,INDEX idx_user_time (user_id, create_time)
);

以下查询都可以 避免排序

-- ✔️ 固定 user_id,按时间排序
SELECT * FROM orders 
WHERE user_id = 123 
ORDER BY create_time DESC;-- ✔️ 按 user_id 分组,统计最早下单时间
SELECT user_id, MIN(create_time) 
FROM orders 
GROUP BY user_id;-- ✔️ 范围查询 + 正序输出
SELECT * FROM orders 
WHERE create_time > '2024-01-01' 
ORDER BY create_time;

但这个不行:

-- ❌ create_time 不是最左前缀
SELECT * FROM orders 
WHERE create_time > '2024-01-01' 
ORDER BY user_id;

需要加额外索引才能优化。


如果你愿意,我可以根据你的具体表结构和查询语句,帮你分析是否能用索引优化排序或分组。

希望这份解读能帮你彻底理解那几段技术文档 😊

http://www.dtcms.com/a/450068.html

相关文章:

  • 什么是营销型网站呢什么网站做新产品代理
  • 海沧建设网站多少jetpack报错 wordpress
  • 从零起步学习Redis || 第九章:缓存雪崩,缓存击穿,缓存穿透三大问题的成因及实战解决方案
  • 手机网站 微信链接网站建设工具
  • 网站建设年度总结客源通app下载
  • 欧美做暧网站jsp可以做网站吗
  • Variational Quantum Eigensolver笔记
  • 操作系统应用开发(二十四)RustDesk 404错误—东方仙盟筑基期
  • 网站菜单样式关于网站策划的文章
  • 做网站多久才会有收益网站打开显示站点目录
  • python异常
  • STM32CubeMonitor使用记录
  • 网站这么做海城seo网站排名优化推广
  • 做网站后的收获宁波seo软件免费课程
  • 网站设计精美案例常见软件开发模型有哪些
  • dedecms 网站还原数据之后 乱码网站开发用什么编程
  • 美发店会员管理软件培训
  • 建设部网站不支持360深圳市南山区住房和建设局网站官网
  • 电子签名法律实务全解析:从合规基础到风险防控的实战指南
  • 1元购网站建设广西壮族自治区市场监督管理局官网
  • 陕西省交通建设集团公司门户网站wordpress站内统计插件
  • PyTorch梯度裁剪与避免Loss为NaN的完整指南
  • 【UE5】新建Editor Standalone Window插件,之前(或当前)创建插件的按钮消失(被顶掉/占用)的问题
  • h5做的网站如何连接数据库wordpress上传图片错误媒体库错误
  • 预约记录关联查询接口说明
  • 免得做网站云浮新增病例详情
  • <从零基础到精通JavaScript>1.1 JavaScript 运行环境
  • 青浦专业网站建设企业网站最下面的那栏叫啥
  • 每日一个C语言知识:C 数据类型
  • 实验二十 GaussDB逻辑备份恢复实验