📌 今日关键词: 执行计划、Explain、SQL优化、索引失效、性能诊断

大家好呀!我是​数据库小学妹​👋

我们之前学过​索引​,也分享过​SQL优化十大技巧​。但是在使用索引的过程我常遇到这些问题:

我怎么知道​索引有没有生效​?为什么加了索引还是慢?这条查询到底慢在哪一步?

每次遇到慢查询,我都像无头苍蝇一样乱试:加索引、改SQL、拆表……运气好能蒙对,运气不好折腾半天。

后来同事告诉我:光建索引不行,你得学会看SQL的“体检报告”,我们今天要学的就是数据库的“诊断报告”——​执行计划​。把SQL丢进去,它就会告诉你数据库打算怎么查、用没用索引、扫了多少行。精准定位查询瓶颈,不再瞎优化!

一、什么是执行计划?

执行计划是数据库为了执行你的SQL,自己制定的“作战方案”。它会告诉你:

  • 什么顺序访问表
  • 用​哪个索引​(或者不用)
  • 预计要扫描多少行
  • 需不需要​临时表​、文件排序

如何查看? 很简单,在SELECT前面加上 EXPLAIN 就行了:

EXPLAIN SELECT * FROM users WHERE age > 18;

执行后,MySQL会返回一张表格,每一行代表一个“步骤”。看懂这张表,你就知道SQL慢在哪了。

💡 EXPLAIN 不会真正执行SQL,只是分析查询计划,所以放心用,不会改数据。

二、执行计划结果中6个关键字段

虽然 EXPLAIN 返回的字段很多,但我们新手只需要关注​6个关键字段​,就能解决90%的问题:

⭐ id:执行顺序的“优先级”

  • 含义: 查询中操作表的顺序标识。
  • 怎么看: 数字越大,越先执行;数字相同,从上往下执行。
  • 避坑: 如果你在写复杂的子查询,id 能帮你判断哪个子查询是“老大”。

⭐select_type:查询的“身份”

  • SIMPLE:简单的查询(没有子查询或UNION)。
  • PRIMARY:最外层的查询。
  • SUBQUERY:子查询中的第一个 SELECT。
  • 理解它: 知道自己写的是简单查询还是复杂嵌套,有助于分析性能。

⭐table:正在操作的“对象”

  • 含义: 这一行操作的是哪张表。
  • 技巧: 在多表关联时,这里会显示表名,配合 type 字段看关联效率。

⭐type:访问类型的“含金量”(最重要!)

这是判断SQL性能好坏的金标准。从最好到最差:

  • system/const:完美! 通过主键或唯一索引直接命中,速度极快。
  • eq_ref:优秀! 通常出现在多表关联的主键匹配中。
  • ref:良好! 使用了非唯一索引进行等值查询。
  • range:凑合! 使用了索引进行范围查询(如 BETWEEN, IN)。
  • index:警告! 索引全扫描。虽然走了索引,但还是把索引树遍历了一遍。
  • ALL:危险! 全表扫描!这是性能杀手,必须优化。

小学妹口诀: 能 const 就不 range,千万别出现 ALL!

⭐ possible_keys & key:索引的“博弈”

  • possible_keys:理论上可能用到的索引。
  • key:实际上真正用到的索引。
  • 避坑: 如果 possible_keys 有值,但 key 是 NULL,说明索引失效了! 这就是你需要排查的地方。

⭐rows:预计扫描的“行数”

  • 含义: 数据库预估要扫描多少行才能找到数据。
  • 目标: 这个数字越小越好。如果是百万级的表,这里显示几万,那肯定有问题。

三、实战:用Explain诊断“慢SQL”

场景: 我们有一张orders表(订单表),数据量很大。我想查用户ID为100的所有订单。

  1. 建表示例
CREATE TABLE orders (
    id INT PRIMARY KEY,
    user_id INT,
    amount DECIMAL(10,2),
    create_time DATETIME
);
-- 假设我们忘了给 user_id 加索引
  1. 慢SQL诊断
EXPLAIN SELECT * FROM orders WHERE user_id = 100;

报告分析:

🚨 诊断结果:

  • type: ALL:全表扫描!数据库正在一行一行地翻找。
  • key: NULL:没有用到任何索引。
  • rows: 1000000:扫描了100万行!这太慢了。
  1. 优化方案 给 user_id 加个索引,再跑一次 EXPLAIN:
CREATE INDEX idx_user_id ON orders(user_id);
EXPLAIN SELECT * FROM orders WHERE user_id = 100;

优化后报告:

✅ 优化结果:

  • type: ref:使用了索引!
  • key: idx_user_id:命中了我们刚建的索引。
  • rows: 5:只扫描了5行数据!🚀 这就是 EXPLAIN 的魔力!

四、新手避坑指南

💢对字段使用了函数:

  • 错误: WHERE YEAR(create_time) = 2023
  • 原因: 数据库无法直接用索引,必须把所有时间拿出来算一遍年份。
  • 修正: WHERE create_time BETWEEN ‘2023-01-01’ AND ‘2023-12-31’

💢最左前缀原则破坏:

  • 场景: 你建了联合索引 (a, b, c),但查询条件只写了 WHERE b = 1 AND c = 2。
  • 结果: 索引失效(除非是覆盖索引)。
  • 修正: 查询条件必须包含最左边的字段 a。

💢隐式类型转换:

  • 错误: user_id 是 INT 类型,但你写了 WHERE user_id = ‘100’(加了引号)。
  • 原因: 数据库会把数字转成字符串比较,导致索引失效。
  • 修正: 保持类型一致,WHERE user_id = 100。

五、今日学习心得

今天的内容总结成三句话:

  1. EXPLAIN 是慢查询的“CT机”​,用之前不要乱加索引
  2. 重点关注 type(ALL要优化)、key(是否为NULL​**)、Extra(有无临时表/文件排序)**
  3. 优化后一定重新 EXPLAIN​,验证是否生效

👋 我是数据库小学妹一个用设计师思维学数据库的转行人。我们一起,把复杂的技术变得简单有趣!💕

本文示例基于 MySQL 8.0。EXPLAIN 在不同版本中细节有差异,但核心思想通用。

Logo

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

更多推荐