MySQL EXPLAIN 深度解析与 SQL 优化实战

目录

[一、引言:EXPLAIN------MySQL 性能优化的X光机](#一、引言:EXPLAIN——MySQL 性能优化的X光机)

[二、EXPLAIN 基础入门](#二、EXPLAIN 基础入门)

[2.1 什么是 EXPLAIN](#2.1 什么是 EXPLAIN)

[2.2 基本语法](#2.2 基本语法)

[2.3 输出格式](#2.3 输出格式)

三、核心字段详解

[3.1 id](#3.1 id)

[3.2 select_type](#3.2 select_type)

[3.3 type](#3.3 type)

[3.4 possible_keys 和 key](#3.4 possible_keys 和 key)

[3.5 key_len](#3.5 key_len)

[3.6 ref](#3.6 ref)

[3.7 rows](#3.7 rows)

[3.8 filtered](#3.8 filtered)

[四、Extra 字段深度解析](#四、Extra 字段深度解析)

[4.1 Using index](#4.1 Using index)

[4.2 Using where](#4.2 Using where)

[4.3 Using temporary](#4.3 Using temporary)

[4.4 Using filesort](#4.4 Using filesort)

[4.5 Using join buffer (Block Nested Loop)](#4.5 Using join buffer (Block Nested Loop))

[4.6 Using index condition](#4.6 Using index condition)

[4.7 Using index for group-by](#4.7 Using index for group-by)

[4.8 Select tables optimized away](#4.8 Select tables optimized away)

[4.9 Range checked for each record](#4.9 Range checked for each record)

[4.10 Using union](#4.10 Using union)

五、实战案例分析

[5.1 全表扫描优化](#5.1 全表扫描优化)

[5.2 分页查询优化](#5.2 分页查询优化)

[5.3 索引失效优化](#5.3 索引失效优化)

[5.4 子查询优化](#5.4 子查询优化)

[5.5 排序分组优化](#5.5 排序分组优化)

[六、高级 SQL 优化技巧](#六、高级 SQL 优化技巧)

[6.1 索引设计黄金法则](#6.1 索引设计黄金法则)

[6.2 避免索引失效的常见误区](#6.2 避免索引失效的常见误区)

[6.3 数据库配置优化](#6.3 数据库配置优化)

七、总结与建议

[7.1 核心总结](#7.1 核心总结)

[7.2 性能优化流程](#7.2 性能优化流程)

[7.3 持续优化建议](#7.3 持续优化建议)


一、引言:EXPLAIN------MySQL 性能优化的X光机

在互联网系统中,数据库性能往往是决定系统吞吐量和响应时间的关键因素。而在 MySQL 性能优化领域,EXPLAIN 命令无疑是最强大的工具之一。它就像是给 SQL 语句做了一次 CT 扫描,能够将 MySQL 执行 SQL 时的内部机制完全暴露出来,包括索引使用情况、表连接顺序、数据访问方式等核心信息。

然而,大多数开发者对 EXPLAIN 的理解仅仅停留在"看是否走了索引"这个层面,很少能够深入理解每个字段背后的含义,更不用说利用这些信息进行系统的 SQL 优化。本文将带你全面深入地了解 EXPLAIN 的所有字段,特别是对 Extra 字段进行详细解析,并结合大量实战案例,教你如何通过 EXPLAIN 快速定位并解决 SQL 性能问题。

二、EXPLAIN 基础入门

2.1 什么是 EXPLAIN

EXPLAIN 是 MySQL 提供的一个强大工具,用于分析 SQL 查询语句的执行计划。通过在 SELECTDELETEINSERTUPDATE 等语句前添加 EXPLAIN 关键字,MySQL 会返回该 SQL 语句的执行计划信息,而不是实际执行该语句。

执行计划中包含了 MySQL 优化器对 SQL 语句的理解和处理方式,包括:

  • MySQL 如何查找表中的行

  • 使用了哪些索引

  • 表之间的连接顺序和连接类型

  • 扫描的行数估计

  • 需要进行的额外操作(如排序、临时表等)

2.2 基本语法

sql 复制代码
EXPLAIN SELECT * FROM users WHERE age > 18;

-- 或者使用 DESCRIBE(简写为 DESC)
DESCRIBE SELECT * FROM users WHERE age > 18;

2.3 输出格式

EXPLAIN 的输出结果是一个表格,包含以下字段:

字段名 含义 重要程度
id SELECT 查询的序列号 ★★★
select_type 查询类型 ★★★★
table 访问的表名或别名 ★★★
partitions 匹配的分区
type 数据访问类型(关键性能指标) ★★★★★
possible_keys 可能使用的索引 ★★★
key 实际使用的索引 ★★★★★
key_len 使用的索引字节数 ★★★
ref 与索引比较的列或常量 ★★★
rows 预估扫描行数 ★★★★
filtered 存储引擎层过滤后剩余数据的百分比 ★★★
Extra 额外执行信息(关键优化提示) ★★★★★

三、核心字段详解

3.1 id

含义 :SELECT 查询的序列号,标识查询中 SELECT 子句的执行顺序。

执行顺序规则

  1. id 相同:按从上到下的顺序执行

  2. id 不同:id 值越大,执行优先级越高(如子查询)

  3. id 为 NULL:最后执行,表示结果集合并操作(如 UNION 的结果)

示例

sql 复制代码
EXPLAIN SELECT * FROM (SELECT * FROM users) AS t_derived JOIN orders o ON t_derived.id = o.user_id;

执行顺序解析

  • id=1:主查询,执行连接操作

  • id=2:子查询,先执行子查询生成派生表

3.2 select_type

含义 :表示 SELECT 查询的类型,反映查询的复杂程度。

常见值及其含义

select_type 含义
SIMPLE 简单查询,不包含子查询或 UNION
PRIMARY 复杂查询中最外层的查询
SUBQUERY SELECTWHERE 列表中的子查询(非关联子查询)
DEPENDENT SUBQUERY 依赖外层查询的子查询(关联子查询,性能较差)
DERIVED FROM 列表中包含的子查询,MySQL 会将结果放在一张临时表中(派生表)
UNION UNION 中的第二个或后续查询
UNION RESULT UNION 的结果集

优化建议

  • 避免使用 DEPENDENT SUBQUERY,因为这种子查询需要为外层查询的每一行执行一次,性能较差

  • 尽量将子查询改写为 JOIN 操作

3.3 type

含义 :表示 MySQL 查找表中数据的方式,是 EXPLAIN 结果中最关键的列之一,直接反映查询的性能。

性能排序(从优到劣)

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

常见值详解

  1. system

    • 表示表只有一行记录(系统表),这是 const 的特例

    • 性能最优,但实际业务中很少出现

  2. const

    • 通过主键或唯一索引一次就能找到匹配的行

    • 适用于将主键置于 WHERE 条件中的查询

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

    • 唯一索引关联查询,对于驱动表的每一行,被驱动表最多返回一条匹配记录
    • 适用于 JOIN 查询且关联字段为主键或唯一索引的情况
    sql 复制代码
    EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id;
  4. ref

    • 非唯一索引查找,返回所有匹配某个单独值的行

    • 适用于普通索引查询或关联查询的非主键/非唯一索引字段

    sql 复制代码
    EXPLAIN SELECT * FROM users WHERE age = 25; -- age 字段有普通索引
  5. range

    • 索引范围扫描,常用于 BETWEEN><INOR 等操作
    sql 复制代码
    EXPLAIN SELECT * FROM users WHERE id > 100;
  6. index

    • 全索引扫描,遍历整个索引树,通常比全表扫描快

    • 适用于只需要读取索引字段的查询(覆盖索引)

    sql 复制代码
    EXPLAIN SELECT id FROM users; -- id 是主键
  7. ALL

    • 全表扫描,性能最差,需要优先优化

    • 表示 MySQL 需要遍历整张表来找到匹配的行

优化建议

  • 一般需要保证查询至少达到 range 级别,最好能达到 ref 或更高

  • 绝对避免出现 ALL 类型的查询,这意味着没有使用任何索引,进行了全表扫描

3.4 possible_keys 和 key

possible_keys :表示 MySQL 优化器认为可能适用的索引,但并不一定在查询时实际使用。如果该字段为 NULL,表示没有适用的索引。

key :表示 MySQL 实际选择使用的索引。如果该字段为 NULL,表示没有使用索引,MySQL 可能执行了全表扫描。

优化建议

  • possible_keys 有值但 keyNULL 时,说明 MySQL 认为索引的选择性较差,没有使用索引。这时需要检查索引是否合理,或者是否有必要调整 SQL 语句。

  • 可以通过 FORCE INDEX 强制使用某个索引,或通过 IGNORE INDEX 忽略某个索引。

sql 复制代码
-- 强制使用 idx_age 索引
EXPLAIN SELECT * FROM users FORCE INDEX(idx_age) WHERE age = 25;

3.5 key_len

含义:表示 MySQL 在索引中使用的字节数,可以通过该值计算出具体使用了索引中的哪些列。

计算规则

  • 整数类型:

    • TINYINT:1 字节

    • SMALLINT:2 字节

    • INT:4 字节

    • BIGINT:8 字节

  • 字符串类型:

    • VARCHAR(n):根据字符集不同而不同,例如 utf8mb4 每个字符占 4 字节,再加上 2 字节的长度标志

    • CHAR(n):固定长度,utf8mb4 每个字符占 4 字节

  • 允许 NULL 的字段需要额外加 1 字节的 NULL 标志

示例

sql 复制代码
ALTER TABLE users ADD INDEX idx_name_age(name, age);
EXPLAIN SELECT * FROM users WHERE name = '张三' AND age = 25;

如果 nameVARCHAR(10)utf8mb4),ageINT,则 key_len 的计算为:

10 * 4 + 2 + 4 + 1 = 47

  • 10 * 4name 字段占用的字节数

  • + 2VARCHAR 类型的长度标志

  • + 4age 字段占用的字节数

  • + 1:允许 NULL 的标志(如果字段不允许 NULL 则不加)

3.6 ref

含义:表示与索引比较的列或常量,显示哪些列或常量被用于查找索引列上的值。

常见值

  • const:表示使用常量值进行匹配

  • 列名:表示使用其他表的列进行比较

  • func:表示使用函数结果进行匹配

示例

sql 复制代码
-- ref: const
EXPLAIN SELECT * FROM users WHERE id = 1;

-- ref: db1.users.id
EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id;

3.7 rows

含义 :表示 MySQL 预估的为了找到所需行需要读取的行数。对于 InnoDB 表来说,这只是一个估计值,可能与实际情况有一定偏差。

优化建议

  • 该值越小越好,数值越大表示查询的性能越差

  • 如果 rows 与实际行数偏差过大,可以考虑执行 ANALYZE TABLE 更新表统计信息

3.8 filtered

含义 :表示存储引擎层过滤后剩余数据的百分比(0~100)。例如,如果 rows 是 1000,filtered 是 10.00(即 10%),表示存储引擎返回了 1000 行,但在服务层过滤后只剩下了 100 行。

优化建议

  • filtered 值越低,表示服务层需要过滤的数据越多,性能越差

  • 如果 filtered 值较低,可以考虑优化 WHERE 条件,尽量让更多的过滤在存储引擎层完成(通过索引下推等特性)

四、Extra 字段深度解析

Extra 字段是 EXPLAIN 结果中最重要的字段之一,包含了大量关于查询执行的额外信息,是发现查询性能问题和优化的关键。

4.1 Using index

含义:表示 MySQL 使用了覆盖索引(Covering Index),直接从索引中返回所需列,不需要回表访问数据行。

优化建议

  • 这是一种高效的访问方式,说明查询语句写得比较好,索引设计合理

  • 可以进一步扩展索引以覆盖更多常用查询列,减少回表操作

4.2 Using where

含义 :表示 MySQL 在存储引擎检索行后需要通过 WHERE 条件进一步过滤数据。

常见场景

  • 查询没有使用全部索引列,需要额外过滤

  • 索引范围扫描后,还需在存储引擎层做行级过滤

优化建议

  • 尽量让过滤条件在索引层完成,减少回表操作

  • 可以使用索引条件下推(ICP)特性,让更多过滤条件在索引层完成

4.3 Using temporary

含义 :表示 MySQL 需要创建临时表来保存中间结果。这通常出现在含有 GROUP BYDISTINCTORDER BY 等子句的复杂查询中,尤其是当这些子句中的列没有合适的索引时。

常见场景

  • GROUP BY 且分组字段没有合适的索引

  • ORDER BY 且排序字段没有合适的索引

  • DISTINCT 操作

  • 复杂子查询或多表 JOIN

优化建议

  • GROUP BYORDER BY 子句中的列添加适当的索引

  • 检查是否可以简化查询,避免使用临时表

  • 适当增加 tmp_table_sizemax_heap_table_size 参数值,让临时表尽可能在内存中处理

4.4 Using filesort

含义 :表示 MySQL 需要额外的一次传递来进行排序。当 ORDER BY 子句中的列不是索引的一部分,或者索引的顺序与 ORDER BY 要求的顺序不一致时,就会出现这种情况。

常见场景

  • ORDER BY 字段未使用索引

  • 排序字段与 WHERE 条件列不在同一个索引

  • 排序方向不一致(ASC/DESC 混合)

优化建议

  • ORDER BY 子句中的列创建合适的索引

  • 确保索引的列顺序与 ORDER BY 子句的顺序一致,且方向相同

  • 必要时使用"反向列"存储倒序值,保证统一方向

4.5 Using join buffer (Block Nested Loop)

含义 :表示 MySQL 在做 JOIN 时使用了连接缓存区,因为被驱动表没有可用索引。

常见场景

  • 被驱动表(JOIN 后的表)关联字段无索引

  • 多表关联条件不走索引

优化建议

  • JOINON 条件字段上建立索引

  • 调整表顺序,让小表做驱动表

  • 考虑分步查询减少 JOIN

4.6 Using index condition

含义:表示 MySQL 使用了索引条件下推(Index Condition Pushdown, ICP)优化。MySQL 会在存储引擎层使用索引中的信息来预先过滤记录,减少回表操作。

优化建议

  • 这是一种优化手段,说明 MySQL 正在使用 ICP 特性

  • 确保索引包含所有可能的过滤条件字段,进一步减少回表操作

4.7 Using index for group-by

含义:表示 MySQL 利用索引顺序直接完成分组,不需要额外排序。

常见场景

  • GROUP BY 的列正好是索引的前缀

优化建议

  • GROUP BY 列的顺序建立索引,充分利用索引的有序性

4.8 Select tables optimized away

含义:表示 MySQL 在优化阶段就直接计算了结果,不需要真正访问表。

常见场景

  • 查询聚合函数(如 MIN/MAX/COUNT)且有索引

  • 简单的常量查询

优化建议

  • 这种情况不需要额外优化,说明 MySQL 已经对查询进行了最大限度的优化

4.9 Range checked for each record

含义 :表示 MySQL 找不到好的索引,需要对驱动表的每一行检查被驱动表中行的范围。这通常意味着 SQL 语句的 JOIN 部分效率低下。

优化建议

  • JOIN 条件相关的列上创建索引

  • 调整查询结构,拆分为多个简单查询

  • 执行 ANALYZE TABLE 更新表统计信息

4.10 Using union

含义:表示 MySQL 使用了索引合并优化方法,将多个单列索引的结果集进行合并(取并集)。

常见场景

  • WHERE 条件中是 OR 连接的不同索引列

优化建议

  • 优先使用复合索引替代索引合并,因为复合索引的性能通常更好

  • 如果无法使用复合索引,可以考虑将 OR 条件拆分为多个 UNION 查询

五、实战案例分析

5.1 全表扫描优化

问题场景:某电商系统的订单查询页面响应缓慢,查询 SQL 如下:

sql 复制代码
EXPLAIN SELECT * FROM orders WHERE status = 'completed';

执行计划

TypeScript 复制代码
id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows  | Extra
---|-------------|--------|------|---------------|------|---------|------|-------|-------
1  | SIMPLE      | orders | ALL  | NULL          | NULL | NULL    | NULL | 100000| Using where

问题分析

  • typeALL,表示进行了全表扫描

  • possible_keysNULL,说明没有可用的索引

  • rows 为 100000,需要扫描 10 万行数据

优化方案

status 字段添加索引:

sql 复制代码
ALTER TABLE orders ADD INDEX idx_status(status);

优化后执行计划

TypeScript 复制代码
id | select_type | table  | type | possible_keys | key       | key_len | ref  | rows | Extra
---|-------------|--------|------|---------------|-----------|---------|------|------|-------
1  | SIMPLE      | orders | ref  | idx_status    | idx_status| 62      | const| 1000 | Using where

优化效果

  • typeALL 提升为 ref

  • rows 从 100000 下降为 1000

  • 查询时间从 1.2 秒下降到 0.01 秒

5.2 分页查询优化

问题场景:电商商品列表分页查询,当页数较多时,查询速度变慢:

sql 复制代码
EXPLAIN SELECT * FROM goods WHERE category_id = 10 LIMIT 10000, 10;

执行计划

TypeScript 复制代码
id | select_type | table | type | possible_keys | key        | key_len | ref  | rows  | Extra
---|-------------|-------|------|---------------|------------|---------|------|-------|-------
1  | SIMPLE      | goods | ALL  | NULL          | NULL       | NULL    | NULL | 100000| Using where; Using filesort

问题分析

  • LIMIT 10000, 10 表示 MySQL 需要扫描前 10010 条记录,然后丢弃前 10000 条,只返回最后 10 条

  • 这种深分页方式的性能会随着页数的增加急剧下降

优化方案

方案一:使用主键过滤(适用于连续主键)

sql 复制代码
SELECT * FROM goods 
WHERE category_id = 10 AND goods_id > 10000 
LIMIT 10;

方案二:延迟关联(适用于非连续主键)

sql 复制代码
SELECT g.goods_id, g.name, g.price 
FROM (SELECT goods_id FROM goods WHERE category_id = 10 LIMIT 10000, 10) AS t 
JOIN goods g ON t.goods_id = g.goods_id;

优化效果

查询时间从 500ms 下降到 10ms,性能提升 50 倍以上。

5.3 索引失效优化

问题场景 :用户登录查询,表中已经为 phone 字段创建了索引,但查询速度依然很慢:

sql 复制代码
EXPLAIN SELECT user_id, username FROM user WHERE phone = 13800138000;

执行计划

TypeScript 复制代码
id | select_type | table | type | possible_keys | key  | key_len | ref  | rows  | Extra
---|-------------|-------|------|---------------|------|---------|------|-------|-------
1  | SIMPLE      | user  | ALL  | NULL          | NULL | NULL    | NULL | 1000000| Using where

问题分析

  • typeALL,表示进行了全表扫描

  • possible_keysNULL,说明索引失效

原因排查

查看表结构发现 phone 字段是 VARCHAR 类型,但查询时使用了数字类型进行比较,导致隐式类型转换,索引失效。

优化方案

sql 复制代码
-- 确保查询条件与字段类型一致
EXPLAIN SELECT user_id, username FROM user WHERE phone = '13800138000';

优化后执行计划

TypeScript 复制代码
id | select_type | table | type | possible_keys | key            | key_len | ref  | rows | Extra
---|-------------|-------|------|---------------|----------------|---------|------|------|-------
1  | SIMPLE      | user  | const| idx_user_phone| idx_user_phone | 34      | const| 1    |

优化效果

  • type 变为 const,表示通过主键或唯一索引一次就找到

  • 查询时间从 0.8 秒下降到 0.001 秒,性能提升 800 倍

5.4 子查询优化

问题场景:查询所有有订单的用户信息:

sql 复制代码
EXPLAIN SELECT * FROM products WHERE id IN (SELECT product_id FROM orders);

执行计划

TypeScript 复制代码
id | select_type        | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra
---|-------------------|-----------|------|---------------|------|---------|------|--------|-------
1  | PRIMARY           | products  | ALL  | NULL          | NULL | NULL    | NULL | 10000  | Using where
2  | DEPENDENT SUBQUERY| orders    | ref  | idx_product_id| idx_product_id| 4      | func  | 10000  |

问题分析

  • select_typeDEPENDENT SUBQUERY,表示这是一个关联子查询

  • 这种子查询需要为外层查询的每一行执行一次,性能较差

优化方案

将子查询改写为 JOIN 操作:

复制代码
EXPLAIN SELECT DISTINCT p.* FROM products p JOIN orders o ON p.id = o.product_id;

优化后执行计划

TypeScript 复制代码
id | select_type | table  | type | possible_keys | key            | key_len | ref          | rows  | Extra
---|-------------|--------|------|---------------|----------------|---------|--------------|-------|-------
1  | SIMPLE      | o      | ALL  | NULL          | NULL           | NULL    | NULL         | 100000| Using temporary
2  | SIMPLE      | p      | eq_ref| PRIMARY       | PRIMARY        | 4       | db1.o.product_id| 1    |

优化效果

  • 避免了关联子查询的性能问题

  • 查询时间从 2.3 秒下降到 0.2 秒,性能提升 10 倍以上

5.5 排序分组优化

问题场景:查询每个部门薪资最高的员工:

sql 复制代码
EXPLAIN SELECT * FROM employees e1 
WHERE salary = (SELECT MAX(salary) FROM employees e2 WHERE e2.department_id = e1.department_id);

执行计划

TypeScript 复制代码
id | select_type        | table     | type | possible_keys | key  | key_len | ref  | rows  | Extra
---|-------------------|-----------|------|---------------|------|---------|------|-------|-------
1  | PRIMARY           | e1        | ALL  | NULL          | NULL | NULL    | NULL | 10000 | Using where
2  | DEPENDENT SUBQUERY| e2        | ref  | idx_department| idx_department| 4      | func  | 1000  | Using index condition

问题分析

  • 使用了关联子查询,性能较差

  • 需要对每个部门执行一次子查询

优化方案

使用窗口函数(MySQL 8.0+)进行优化:

sql 复制代码
EXPLAIN WITH ranked_employees AS (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) as rn 
  FROM employees
)
SELECT * FROM ranked_employees WHERE rn = 1;

优化效果

  • 只需要扫描一次表即可完成查询

  • 查询时间从 3.5 秒下降到 0.3 秒,性能提升 10 倍以上

六、高级 SQL 优化技巧

6.1 索引设计黄金法则

1. 最左前缀匹配

复合索引的使用遵循最左前缀原则,即 INDEX(a,b,c) 可以支持 aa,ba,b,c 的查询,但不能支持 bcb,c 的查询。

2. 选择性优先

选择性高的列适合作为索引的前缀,区分度高的列(如用户 ID、订单号)适合建索引,而区分度低的列(如性别、状态)不适合单独建索引

3. 覆盖索引

如果查询的列完全包含在索引中,MySQL 可以直接使用索引而无需回表,这种索引称为覆盖索引。

6.2 避免索引失效的常见误区

1. 不要在索引列上使用函数或计算

sql 复制代码
-- 错误写法(索引失效)
SELECT * FROM users WHERE YEAR(create_time) = 2023;

-- 正确写法(索引有效)
SELECT * FROM users WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';

2. 避免使用 !=<> 操作符

这些操作符会导致索引失效,尽量使用范围查询替代。

3. 注意隐式类型转换

查询条件的类型要与字段类型一致,否则会导致索引失效。

4. 避免使用 OR 连接非索引字段

如果 OR 连接的字段中有一个没有索引,则会导致整个查询的索引失效。

5. LIKE 以通配符开头会导致索引失效

sql 复制代码
-- 错误写法(索引失效)
SELECT * FROM users WHERE username LIKE '%陈%';

-- 正确写法(索引有效)
SELECT * FROM users WHERE username LIKE '陈%';

6.3 数据库配置优化

1. 调整缓冲区大小

  • innodb_buffer_pool_sizeInnoDB 的缓冲池大小,建议设置为物理内存的 50~70%

  • sort_buffer_size:排序缓冲区大小

  • join_buffer_size:连接缓冲区大小

2. 开启慢查询日志

sql 复制代码
-- 开启慢查询日志
slow_query_log = 1
-- 设置慢查询阈值(秒)
long_query_time = 2
-- 记录没有使用索引的查询
log_queries_not_using_indexes = 1

3. 其他优化参数

  • tmp_table_sizemax_heap_table_size:设置临时表的最大内存大小

  • innodb_log_file_sizeInnoDB 的日志文件大小

  • query_cache_size:查询缓存大小(MySQL 8.0+ 已移除)

七、总结与建议

7.1 核心总结

  • type 字段是关键 :优先关注 type 字段,确保查询至少达到 range 级别,最好能达到 ref 或更高

  • 索引是优化的核心:合理的索引设计是提高查询性能的关键,要遵循最左前缀原则,选择区分度高的列作为索引

  • Extra 字段是优化的重点 :特别关注 Using filesortUsing temporary,这两个是性能杀手,需要尽快优化

  • 避免全表扫描 :绝对避免出现 type=ALL 的查询,这意味着没有使用任何索引,进行了全表扫描

7.2 性能优化流程

  1. 捕获慢 SQL:通过慢查询日志或性能监控工具发现慢查询

  2. EXPLAIN 分析 :使用 EXPLAIN 查看执行计划,定位性能瓶颈

  3. 索引优化:添加缺失的索引,调整索引顺序,使用覆盖索引

  4. SQL 重写 :将子查询改写为 JOIN,优化 WHERE 条件,避免索引失效

  5. 配置调整:根据需要调整数据库配置参数

  6. 验证效果 :使用 EXPLAIN 或实际执行时间验证优化效果

7.3 持续优化建议

  • 定期分析慢查询日志:每周或每月分析慢查询日志,发现潜在的性能问题

  • 更新统计信息 :定期执行 ANALYZE TABLE 更新表统计信息,帮助 MySQL 优化器做出更准确的选择

  • 监控系统性能:使用性能监控工具(如 Prometheus + Grafana)实时监控数据库性能

  • 持续学习:关注 MySQL 官方文档和性能优化的最新技术,不断提升自己的优化能力

通过本文的学习,相信你已经掌握了 EXPLAIN 的使用方法和 SQL 优化的核心技巧。在实际工作中,要多使用 EXPLAIN 分析查询计划,不断积累优化经验,才能真正成为 SQL 优化的高手。

最后,记住一个核心原则:能走索引,就不全表扫描;能覆盖索引,就不回表;能浅分页,就不深分页。

祝你的 SQL 优化之路一帆风顺!

相关推荐
2401_832298104 分钟前
《场景为王:云服务器选型的“精准匹配”指南》
mysql
Moresweet猫甜5 分钟前
Ubuntu LVM引导丢失紧急救援:完整恢复指南
linux·运维·数据库·ubuntu
MindCareers6 分钟前
Beta Sprint Day 5-6: Android Development Improvement + UI Fixes
android·c++·git·sql·ui·visual studio·sprint
yumgpkpm9 分钟前
Cloudera CDH5、CDH6、CDP7现状及替代方案
数据库·人工智能·hive·hadoop·elasticsearch·数据挖掘·kafka
松涛和鸣11 分钟前
48、MQTT 3.1.1
linux·前端·网络·数据库·tcp/ip·html
晓时谷雨13 分钟前
达梦数据库适配方案及总结
数据库·达梦·数据迁移
LaLaLa_OvO16 分钟前
spring boot2.0 里的 javax.validation.Constraint 加入 service
java·数据库·spring boot
地球资源数据云27 分钟前
MODIS(MCD19A2)中国2000-2024年度平均气溶胶光学深度数据集
大数据·服务器·数据库·人工智能·均值算法
数据大魔方29 分钟前
【期货量化实战】威廉指标(WR)策略:精准捕捉超买超卖信号(Python源码)
开发语言·数据库·python·算法·github·程序员创富
忘记92630 分钟前
mysql一条sql语句是如何运行的
数据库·mysql