Bootstrap

如何使用 MySQL 的 EXPLAIN 语句进行查询分析?

Explain 属性介绍

explain 主要用来 SQL 分析,它主要的属性详解如下:

  • id : 查询的执行顺序的标识符,值越大优先级越高。简单查询的 id 通常为 1,复杂查询(如包含子查询或 UNION)的 id 会有多个。
  • select_type (重要):查询的类型,如 SIMPLE(简单查询)、PRIMARY(主查询)、SUBQUERY(子查询)等。
  • table : 查询的数据表。
  • type (重要):访问类型,如 ALL(全表扫描)、index(索引扫描)、range(范围扫描)等。一般来说,性能从好到差的顺序是:const > eq_ref > ref > range > index > all
  • possible_keys : 可能用到的索引。
  • key (重要):实际用到的索引。
  • key_len : 用到索引的长度。
  • ref : 显示索引的哪一列被使用。
  • rows (重要):估计要扫描的行数,值越小越好。
  • filtered : 显示查询条件过滤掉的行的百分比。一个高百分比表示查询条件的选择性好。
  • Extra (重要):额外信息,如 Using index (表示使用覆盖索引)、Using where (表示使用 WHERE 条件进行过滤)、Using temporary (表示使用临时表)、Using filesort (表示需要额外的排序步骤)。

type 详解:

  • system:表示查询的表只有一行(系统表)。这是一个特殊的情况,不常见。
  • const:表示查询的表最多只有一行匹配结果。这通常发生在查询条件是 主键 主键 主键 唯一索引 唯一索引 唯一索引,并且是常量比较。
  • eq_ref:表示对于每个来自前一张表的行,MySQL 仅访问一次这个表。这通常发生在连接查询中使用主键或唯一索引的情况下。
  • ref:MySQL 使用非唯一索引扫描来查找行。查询条件使用的索引是非唯一的(如普通索引)。
  • range:表示 MySQL 会扫描表的一部分,而不是全部行。范围扫描通常出现在使用索引的范围查询中(如 BETWEEN 、 >, <, >=, <= )。
  • index:表示 MySQL 扫描索引中的所有行,而不是表中的所有行。即使索引列的值覆盖查询,也需要扫描整个索引。
  • all(性能最差):表示 MySQL 需要扫描表中的所有行,即全表扫描。通常出现在没有索引的查询条件中。

SQL 实战

  1. 建表语句
CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    department_id INT,
    salary DECIMAL(10, 2),
    hire_date DATE,
    INDEX (department_id)
);

  1. 填充数据
  • 脚本下载地址:https://restful.doublefenzhuan.me/public/bad6bf7e-3efa-48ff-9239-8b20bbb812e2-employee_data.sql
  1. 执行分析语句
EXPLAIN SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE department_id = 5 AND salary BETWEEN 50000 AND 100000
ORDER BY salary DESC;
  • 执行结果:
    在这里插入图片描述

分析 EXPLAIN 结果的思路和过程:

  1. 首先看 type 列:
  • ref 表示使用非唯一索引扫描,这个效率还可以,但不是最优的 const
  • 如果看到 ALL 就说明全表扫描,需要立即优化
  • 最好是能看到 const, eq_ref 这样的类型
  1. 分析 rows 列:
  • 当前显示 482 行,需要评估这个数字是否合理
  • 如果 rows 数值过大,说明扫描的行数太多,需要考虑优化
  1. 检查 Extra 列:
  • 出现 “Using where” 说明需要在服务器层进行数据过滤
  • “Using filesort” 表示需要额外的排序操作,应该尽量避免
  • “Using index” 表示使用了覆盖索引,这是较好的情况

优化方案

针对 type=ref 的优化:

  • 当前使用的是单列索引(department_id)
  • 可以创建联合索引: (department_id, salary)
  • 因为查询同时用到这两个字段作为条件

ALTER TABLE employees ADD INDEX idx_dept_salary (department_id, salary);

执行

在这里插入图片描述

分析优化第一次后的结果

  1. 变化对比:
  • type 从 ref 变成了 range:
    • ref 类型特点:表示使用非唯一索引的等值查询,只用到了 department_id = 5 这个等值条件。
    • range 类型特点:表示索引范围扫描,发生在使用 >, <, BETWEEN, IN, LIKE 等操作符时(这里是因为 salary BETWEEN 50000 AND 100000 条件)
  • rows 从 482 减少到 241
  • key 使用了新建的 idx_dept_salary 索引
  • Extra 显示 “Using index condition; Backward index scan”
  1. 优化效果分析:
  • 扫描行数减少了约50%,说明索引效果不错
  • range 类型表示范围扫描,这是因为 salary BETWEEN 条件
  • Backward index scan 说明在处理 ORDER BY salary DESC
;