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

MySQL 执行计划:优化查询性能

一、什么是 MySQL 执行计划?

MySQL 执行计划(Execution Plan)是 MySQL 在执行 SQL 查询时,所采取的具体执行策略。它描述了查询如何从数据库中获取数据,执行的步骤顺序以及使用的索引等信息。通过执行计划,我们可以直观地看到查询语句的执行路径,从而判断是否可以优化。

二、如何查看 MySQL 执行计划?

查看执行计划有几种方式:

  1. EXPLAIN 语句
    使用 EXPLAIN 可以查看单个查询的执行计划。例如:

    EXPLAIN SELECT * FROM employees WHERE department_id = 5;
    

    输出类似如下:

    idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
    1SIMPLEemployeesrefdepartment_ididx_dept4const1000Using where
  2. EXPLAIN ANALYZE
    EXPLAIN ANALYZE 会执行查询并返回执行计划,同时还会给出实际执行时间与行数等信息,帮助更精确地理解查询的性能。例如:

    EXPLAIN ANALYZE SELECT * FROM employees WHERE department_id = 5;
    

    这个命令不仅会显示执行计划,还会输出执行过程中的时间和实际扫描的行数。

  3. SHOW PROFILE
    SHOW PROFILE 是另一种查看查询执行性能的方式,提供了更多关于查询资源使用的信息(需要 MySQL 5.0 及以上版本)。

    SET profiling = 1;
    SELECT * FROM employees WHERE department_id = 5;
    SHOW PROFILE FOR QUERY 1;
    

三、执行计划中的各个参数解释

1. id — 查询的执行顺序

解释id 表示查询执行的顺序,对于单一查询来说,id 只有一个值。而在复杂的查询(如多表连接)中,id 用来表示执行的层次,值越小,执行的优先级越高。

示例

EXPLAIN SELECT e.name, d.name FROM employees e JOIN departments d ON e.department_id = d.id WHERE e.salary > 5000;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const1000Using where
1SIMPLEdepartmentseq_refPRIMARYPRIMARY4employees.department_id1

分析id 代表查询的执行顺序。在这种情况下,employeesdepartments 表的查询是平行的(id 值相同),但由于 employees 表使用 ref 类型连接,departments 使用 eq_ref 连接。

2. select_type — 查询类型

解释select_type 表示查询的类型,主要有以下几种:

  • SIMPLE:简单查询,未使用子查询。
  • PRIMARY:外部查询(如果存在子查询)。
  • UNION:UNION 查询中的第二个及后续查询。
  • SUBQUERY:子查询。

示例

EXPLAIN SELECT e.name FROM employees e WHERE e.department_id IN (SELECT id FROM departments WHERE name = 'Engineering');

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1PRIMARYemployeesrefdepartment_ididx_dept4subquery1000Using where
2SUBQUERYdepartmentsALLNULLNULLNULLNULL5

分析select_type 指明查询类型,其中 PRIMARY 表示主查询,SUBQUERY 表示内层查询。这里内层查询是 SELECT id FROM departments WHERE name = 'Engineering',外层查询则基于该结果进行过滤。

3. table — 查询的表

解释table 显示查询涉及的表名。如果是多表查询,table 会列出每个参与查询的表。

示例

EXPLAIN SELECT e.name, d.name FROM employees e JOIN departments d ON e.department_id = d.id;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const1000
1SIMPLEdepartmentseq_refPRIMARYPRIMARY4employees.department_id1

分析table 显示了查询中涉及的表。在这里,employeesdepartments 是查询的两个表。

4. type — 连接类型

解释type 描述了 MySQL 如何查找表中的行,表示连接的方式。常见的连接类型按效率从高到低排序:

  • const:常量查找,效率最高。
  • eq_ref:精确匹配查找。
  • ref:非唯一匹配查找。
  • range:范围扫描,使用索引。
  • index:全索引扫描。
  • ALL:全表扫描,效率最低。

示例

EXPLAIN SELECT * FROM employees WHERE department_id = 5;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const1000Using where

分析typeref,表示 MySQL 使用索引(idx_dept)通过 department_id 列查找匹配的行。

5. key — 使用的索引

解释key 显示查询实际使用的索引,如果没有使用索引,则显示 NULL

示例

EXPLAIN SELECT * FROM employees WHERE department_id = 5 AND salary > 5000;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const500Using where

分析keyidx_dept,表示查询使用了 department_id 列的索引。

6. key_len — 索引使用的长度

解释key_len 表示查询使用的索引的长度。数值越小,表示索引覆盖的字段越少,查询效率可能越低。

示例

EXPLAIN SELECT * FROM employees WHERE department_id = 5 AND salary > 5000;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const500Using where

分析key_len 为 4,表示 MySQL 使用了长度为 4 字节的索引(可能是 department_id 的索引)。

7. ref — 与哪些列或常数进行匹配

解释ref 显示与当前行匹配的列或常数,通常是连接条件或查询过滤条件。

示例

EXPLAIN SELECT * FROM employees WHERE department_id = 5;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const500Using where

分析refconst,表示查询中 department_id 的值为常量 5

8. rows — 估算扫描的行数

解释rows 表示 MySQL 估算的要扫描的行数。这个值越小,查询性能通常越好。

示例

EXPLAIN SELECT * FROM employees WHERE department_id = 5;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const500Using where

分析rows 显示 MySQL 估算会扫描 500 行数据。

9. Extra — 额外信息

解释Extra 字段显示关于查询执行的额外信息,包括使用了哪些优化技巧和操作。常见的 Extra 字段值包括:

1. Using where — 使用了 WHERE 过滤

Using where 表示查询在数据检索后使用了 WHERE 子句进行进一步的过滤操作。这意味着,在表中获取到的数据行还需经过额外的计算来满足查询条件。

示例:

EXPLAIN SELECT * FROM employees WHERE department_id = 5 AND salary > 5000;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const1000Using where

分析:

  • Using where 表示 MySQL 在扫描到与 department_id = 5 匹配的行之后,还需要通过 WHERE salary > 5000 进一步过滤数据。这通常意味着数据表上可能没有为两个条件列(department_idsalary)创建复合索引。

优化建议:

  • 若经常在这两个字段上进行查询,可以考虑为 department_idsalary 创建复合索引,从而减少查询时的 WHERE 过滤操作。
2. Using index — 使用了索引覆盖

Using index 表示查询操作使用了 覆盖索引(Covering Index),即查询的所有数据都可以从索引中直接获取,而无需回表访问表数据。索引覆盖通常会提高查询效率,因为它避免了从数据表中加载额外的行数据。

示例:

EXPLAIN SELECT department_id, salary FROM employees WHERE department_id = 5;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const1000Using index

分析:

  • Using index 表示查询的所有数据(department_idsalary)可以直接从索引 idx_dept 中获取,避免了额外的回表查询。这通常会显著提高查询性能,尤其是在查询列较少时。

优化建议:

  • 如果查询的列包含在现有索引中,可以通过索引覆盖提高查询性能。如果没有使用索引覆盖,可以考虑修改索引或调整查询,使查询列包含在索引内。
3. Using filesort — 使用了文件排序

Using filesort 表示 MySQL 在执行查询时需要额外的排序操作。即使查询中明确包含了 ORDER BY 子句,MySQL 也可能需要额外的步骤来对结果进行排序。文件排序通常会比索引排序慢,因为它需要将数据加载到内存中或临时文件中进行排序。

示例:

EXPLAIN SELECT * FROM employees ORDER BY salary;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesALLNULLNULLNULLNULL1000Using filesort

分析:

  • Using filesort 表示 MySQL 在执行 ORDER BY salary 时没有使用索引,而是需要对结果集进行文件排序。这可能是因为 salary 上没有索引,或者查询中有其他导致无法使用索引的情况。

优化建议:

  • 如果经常按 salary 排序,可以考虑为 salary 列创建索引,尤其是在大数据量的表中,使用索引可以大大提高排序效率。
4. Using temporary — 使用了临时表

Using temporary 表示 MySQL 在执行查询时使用了临时表。这通常发生在以下几种情况:

  • 查询包含了 GROUP BYDISTINCT 操作。
  • 查询包含了复杂的 JOIN 操作。
  • 查询需要排序,且不能使用索引进行排序。

临时表通常会存储在磁盘中,这会增加 I/O 操作,并降低查询性能。

示例:

EXPLAIN SELECT department_id, COUNT(*) FROM employees GROUP BY department_id;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesALLNULLNULLNULLNULL1000Using temporary, Using filesort

分析:

  • Using temporaryUsing filesort 表示 MySQL 在执行 GROUP BY 时需要创建临时表,并且可能还需要进行文件排序。这通常是因为查询没有使用索引,或者 GROUP BY 聚合操作导致了临时表的创建。

优化建议:

  • 可以为 department_id 创建索引,优化 GROUP BY 操作,减少临时表的使用。
  • 在可能的情况下,避免使用 GROUP BYORDER BY 组合,或者考虑改写查询,减少内存和磁盘上的临时表操作。
5. Using join buffer — 使用了连接缓冲区

Using join buffer 表示在执行 JOIN 操作时,MySQL 使用了连接缓冲区。通常情况下,MySQL 会将连接的两个表缓存到内存中,尤其是在执行 JOIN 操作时。如果 MySQL 使用了连接缓冲区,可能意味着没有适当的索引来加速连接操作。

示例:

EXPLAIN SELECT e.name, d.name FROM employees e JOIN departments d ON e.department_id = d.id;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const1000Using join buffer
1SIMPLEdepartmentseq_refPRIMARYPRIMARY4employees.department_id1

分析:

  • Using join buffer 表示 MySQL 在执行 JOIN 时使用了额外的内存缓冲区。这通常表明 department_id 上缺少适当的索引,导致 MySQL 必须在内存中缓存较大的数据集进行连接操作。

优化建议:

  • 确保 JOIN 操作中涉及的列(如 department_id)上有合适的索引。如果没有,考虑为这些列添加索引,以减少连接操作中的内存使用和执行时间。
6. Using index condition — 使用了索引条件推导

Using index condition 表示 MySQL 在查询过程中能够利用索引条件推导来过滤数据。它会在扫描索引时直接应用查询条件,而不需要访问实际的数据表。这通常提高了查询效率。

示例:

EXPLAIN SELECT * FROM employees WHERE department_id = 5 AND salary > 5000;

执行计划:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEemployeesrefdepartment_ididx_dept4const500Using index condition

分析:

  • Using index condition 表示 MySQL 在扫描索引时就应用了 salary > 5000 的条件,而无需回表查询数据。这是索引条件推导的效果,能够提高查询效率。

优化建议:

  • 确保查询条件能通过索引条件推导来优化,避免回表查询,以提高查询效率。

四、执行计划优化思路

通过分析执行计划,常见的优化方法包括:

1. 添加合适的索引

如果查询频繁使用某些列进行条件过滤或排序,考虑为这些列添加索引。例如,如果 department_id 是常用的过滤条件,应该为其创建索引。

CREATE INDEX idx_dept ON employees(department_id);

2. 避免全表扫描

ALL 类型通常意味着全表扫描,效率较低。此时,应该检查是否有合适的索引可以加速查询。

3. 使用覆盖索引

如果查询只涉及索引中的列,MySQL 可以直接从索引中获取数据,避免回表查询。这种情况下,使用覆盖索引可以显著提高查询效率。

4. 调整查询方式

某些查询在执行时可以通过改写 SQL 语句来优化。例如,避免使用 SELECT *,而应该只选择必要的列。

5. 处理子查询

如果子查询返回大量数据,尝试将其改写为连接查询,或者使用 EXISTS 代替 IN,从而减少不必要的数据扫描。

五、常见问题与解决方案

1. 为什么 type 显示 ALL,查询变得很慢?

ALL 表示 MySQL 执行全表扫描。可能是因为没有合适的索引。检查是否为查询条件列创建了索引,或者是否需要优化查询。

2. 为什么 rows 显示很大,但查询仍然很慢?

这可能意味着查询扫描了大量无效的数据,或者 MySQL 选择了不合适的索引。检查索引是否匹配查询条件,或者尝试使用 EXPLAIN ANALYZE 查找更多细节。

3. 查询执行时间长,如何优化?

  • 使用 EXPLAIN 检查是否有全表扫描。
  • 确认是否有合适的索引。
  • 避免在查询中使用复杂的子查询。
  • 尝试使用 JOIN 替代 INEXISTS,减少查询的复杂度。

结论

MySQL 执行计划是优化查询性能的关键工具,帮助我们了解查询的执行路径。通过分析执行计划中的各个参数,如 typekeyrows 等,我们可以针对性地优化查询。常见的优化方法包括创建索引、避免全表扫描、使用覆盖索引等。掌握执行计划的分析与优化技巧,能大大提高系统的性能和响应速度。


http://www.kler.cn/a/460644.html

相关文章:

  • vue中的设计模式
  • C++ 设计模式:备忘录模式(Memento Pattern)
  • 算法:切饼
  • 【欢迎讨论方案一的可行性】SpringBoot集成netty,在handler中调用@Component注解的类
  • redis的集群模式与ELK基础
  • playwright的page.wait_for 常见用法
  • 家政预约小程序04活动管理表结构设计
  • Mac安装Jupyter和nbextensions报错问题
  • OpenStack系列第四篇:云平台基础功能与操作(Dashboard)
  • Spring 创建和管理 Bean 的原理,以及Spring 的单例模式是否线程安全?(有无状态Bean)
  • 电子电器架构 --- 智能座舱与AI结合
  • 数据仓库工具箱—读书笔记02(Kimball维度建模技术概述05、处理缓慢变化维度SCD属性)
  • 基于深度学习的医疗问诊助手
  • Postman[3] 创建Get和Post请求
  • Django中创建自增主键字段的几种方法
  • UEBA-对等组聚类
  • 数据结构与算法之动态规划: LeetCode 72. 编辑距离 (Ts版)
  • 198.213.337.打家劫舍
  • MySql find_in_set 函数
  • 数据仓库: 9- 数据仓库数据治理
  • KubeOS
  • java基于ThreadLocal实现单例模式
  • Android 系统 AlertDialog 系统层深度定制
  • 基于AT89C51单片机的可暂停八路抢答器设计
  • 试用ChatGPT的copilot编写一个程序从笔记本电脑获取语音输入和图像输入并调用开源大模型进行解析
  • 【一起python】银行管理系统