从零开始搞懂 MySQL 的 explain:type 和 select_type 到底啥意思?

先从最朴素的查询开始

假设我们有个超级简单的表 users,里面存了用户信息:

sql 复制代码
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100)
);

INSERT INTO users (id, name, email) VALUES 
(1, 'Alice', 'alice@example.com'),
(2, 'Bob', 'bob@example.com'),
(3, 'Charlie', 'charlie@example.com');

现在我们跑个最简单的查询:

sql 复制代码
EXPLAIN SELECT * FROM users WHERE id = 1;

结果大概长这样:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE users const PRIMARY PRIMARY 4 const 1 Using index

别慌,咱们先搞懂 select_typetype 是啥。


select_type:查询的类型

select_type 告诉你这条查询在整个执行计划里是个什么角色。简单来说,它描述的是查询的"身份"。常见的值有:

  • SIMPLE :最简单的查询,没有子查询、没有 UNION。比如上面这个例子,就是 SIMPLE,因为它就是一个单表查询。
  • PRIMARY :如果是多表查询或者有子查询,最外层的那个查询会被标记为 PRIMARY
  • SUBQUERY :子查询,比如 WHERE id IN (SELECT id FROM another_table) 里的子查询。
  • UNION :多个查询用 UNION 合并时,第二个及之后的查询会被标记为 UNION

回到上面的例子,select_typeSIMPLE,因为这只是一个单表、无子查询的简单查找。后面我们会看到更复杂的例子。


type:访问方式

type 是重头戏,它告诉你 MySQL 是怎么从表里捞数据的,也就是访问方式。性能从高到低大概是这样排序的:const > eq_ref > ref > range > index > ALL。咱们一个一个讲,顺便配上例子。

1. const
  • 啥意思:MySQL 通过主键或者唯一索引直接定位到一行数据,效率贼高。
  • 例子 :上面的 WHERE id = 1,因为 id 是主键,MySQL 直接找到那行,结果就是 const
  • 形象记忆:就像你在家门口放了个信箱,地址写得清清楚楚,邮递员直接塞进去,不用翻遍整个小区。
2. eq_ref
  • 啥意思:通常在联表查询(JOIN)中,通过主键或唯一索引精确匹配一行。

  • 例子 : 假设还有个表 orders

    sql 复制代码
    CREATE TABLE orders (
        order_id INT PRIMARY KEY,
        user_id INT,
        FOREIGN KEY (user_id) REFERENCES users(id)
    );
    INSERT INTO orders (order_id, user_id) VALUES (101, 1), (102, 2);

    查询:

    sql 复制代码
    EXPLAIN SELECT u.name, o.order_id FROM users u JOIN orders o ON u.id = o.user_id;

    对于 orders 表,type 会是 eq_ref,因为 user_id 通过主键 id 一对一匹配。

  • 结果表

    id select_type table type possible_keys key rows Extra
    1 SIMPLE u ALL PRIMARY NULL 3
    1 SIMPLE o eq_ref user_id user_id 1
  • 形象记忆:就像你拿身份证去银行柜台办业务,柜员直接通过你的唯一号码查到你。

3. ref
  • 啥意思:通过非唯一索引查找,可能返回多行。

  • 例子 : 给 usersname 加个普通索引:

    sql 复制代码
    CREATE INDEX idx_name ON users(name);
    EXPLAIN SELECT * FROM users WHERE name = 'Alice';

    因为 name 不是唯一的,typeref

  • 结果表

    id select_type table type possible_keys key rows Extra
    1 SIMPLE users ref idx_name idx_name 1
  • 形象记忆:就像你在学校找叫"Alice"的同学,可能有好几个,得挨个问。

4. range
  • 啥意思 :通过索引做范围查找,比如 ><BETWEEN

  • 例子

    sql 复制代码
    EXPLAIN SELECT * FROM users WHERE id BETWEEN 1 AND 2;

    typerange,因为用主键查了个范围。

  • 结果表

    id select_type table type possible_keys key rows Extra
    1 SIMPLE users range PRIMARY PRIMARY 2 Using where
  • 形象记忆:就像你在超市找价格在 10 到 20 块之间的商品,得扫一眼货架。

5. index
  • 啥意思:扫描整个索引树,比全表扫描好点,但还是不够快。

  • 例子

    sql 复制代码
    EXPLAIN SELECT name FROM users;

    如果只查索引覆盖的字段,typeindex

  • 形象记忆:像翻电话簿,虽然有索引,但得从头翻到尾。

6. ALL
  • 啥意思:全表扫描,最慢的方式。

  • 例子

    sql 复制代码
    EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com';

    没索引,typeALL

  • 结果表

    id select_type table type possible_keys key rows Extra
    1 SIMPLE users ALL NULL NULL 3 Using where
  • 形象记忆:像大海捞针,一行一行找。


朴素策略的问题

从上面的例子看,最朴素的查询(比如 ALL)效率低得可怕。假设 users 表有 100 万行数据,全表扫描得扫 100 万次,时间成本和资源消耗都爆炸。更别提多表联查了,如果两个表都全扫描,复杂度直接平方级别上升。

不利因子:

  1. 没索引:MySQL 只能挨个儿看每行,像盲人摸象。
  2. 范围不明确 :即使有索引,如果条件太模糊(比如 email LIKE '%alice%'),还是得扫描大量数据。
  3. 联表效率低:没有合适的键关联,JOIN 操作会变成噩梦。

优化方向:逼近主流方案

基于这些问题,优化思路其实跟现在的数据库设计不谋而合:

  1. 加索引

    • 主键和唯一索引让 consteq_ref 成为可能。
    • 普通索引支持 refrange,大幅减少扫描行数。
    • 示例:给 email 加索引,typeALL 变成 ref
  2. 覆盖索引

    • 查询只涉及索引字段时,type 可以是 index,避免回表。
    • 示例:SELECT name FROM usersidx_name 覆盖。
  3. 优化联表

    • 用外键或索引确保 JOIN 用上 eq_refref
    • 示例:orders.user_id 加索引,JOIN 时效率飞升。
  4. 条件收窄

    • 用精确条件(=, IN)替代模糊匹配(LIKE '%...%'),让 range 更高效。

这些优化方向正是现代数据库的标配,比如 B+ 树索引、联合索引、主从架构,都是为了把扫描范围缩到最小,把 type 尽量往 consteq_ref 靠拢。


总结

select_type 是查询的身份标签,type 是捞数据的具体手法。咱们从最简单的 SIMPLEconst 开始,逐步看到 ALL 的低效,再通过索引和条件优化逼近高效方案。记住这些形象比喻,下次看 explain 就能秒懂啦!

相关推荐
CodeSheep22 分钟前
JetBrains官宣,又一个IDE可以免费用了!
前端·后端·程序员
间彧1 小时前
SpringBoot和Servlet的联系
后端
间彧1 小时前
Spring Boot的DispatcherServlet是如何封装和扩展原生Servlet功能的?
后端
无名之辈J1 小时前
GC Overhead 的排查
后端
道19931 小时前
50 台小型无人车与50套穿戴终端 5 公里范围内通信组网方案深度研究
java·后端·struts
间彧1 小时前
Spring Boot中,拦截器和Spring AOP有什么区别
后端
JaguarJack1 小时前
PHP 开发者应该理解的 Linux 入门权限指南
后端·php
IT_陈寒1 小时前
Java性能优化:这5个Spring Boot隐藏技巧让你的应用提速40%
前端·人工智能·后端
程序员爱钓鱼2 小时前
Go语言实战案例——进阶与部署篇:使用Docker部署Go服务
后端·google·go
你的人类朋友10 小时前
先用js快速开发,后续引入ts是否是一个好的实践?
前端·javascript·后端