如何进行 SQL 调优?
如何进行 SQL 调优?
zhangchangsheng面试考察点
- 系统性思维:面试官不仅仅想知道你会不会加索引,更想考察你是否具备从”发现问题 → 定位问题 → 解决问题 → 验证效果”的完整调优思路。
- 工具掌握度:考察你是否熟练使用
EXPLAIN、SHOW PROFILE、慢查询日志等调优工具,能否从执行计划中识别性能瓶颈。 - 原理理解深度:是否理解索引的数据结构(B+ 树)、索引失效场景、MySQL 优化器的工作机制,而非只会照搬网上八股文。
核心答案
MySQL SQL 调优是一个系统化工程,核心流程如下:
flowchart LR
A["开启慢查询日志<br/>slow_query_log=ON"] --> B["定位慢 SQL<br/>mysqldumpslow 分析工具"]
B --> C["分析执行计划<br/>EXPLAIN 执行计划"]
C --> D["优化实施验证<br/>索引 / SQL / 表结构优化"]
上图展示了 SQL 调优的完整流程。整体分为四个阶段:
- 阶段一(开启监控):通过配置
slow_query_log=ON开启慢查询日志,设置long_query_time阈值(默认 10 秒,生产建议 1-3 秒),为后续分析提供数据基础。 - 阶段二(定位问题):使用
mysqldumpslow工具分析慢查询日志,按执行时间、执行次数排序,快速锁定最需要优化的 SQL。 - 阶段三(分析原因):对目标 SQL 执行
EXPLAIN查看执行计划,重点关注type、key、rows、Extra字段,判断是否走索引、扫描行数是否合理。 - 阶段四(优化验证):根据分析结果进行索引优化、SQL 改写、表结构调整,然后再次执行
EXPLAIN验证效果。
一句话总结:调优 = 慢日志定位 + EXPLAIN 分析 + 索引优化 + SQL 改写。
深度解析
一、定位慢 SQL:慢查询日志
首先需要开启慢查询日志,记录执行时间超过阈值的 SQL:
-- 查看慢查询日志配置 |
使用 mysqldumpslow 工具分析慢日志:
# 按查询时间排序,显示前 10 条 |
二、分析执行计划:EXPLAIN 详解
使用 EXPLAIN 或 EXPLAIN ANALYZE(MySQL 8.0+)查看 SQL 执行计划:
EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 'PAID'; |
关键字段解读:
| 字段 | 含义 | 关注点 |
|---|---|---|
| type | 访问类型(效率从高到低) | system > const > eq_ref > ref > range > index > ALL(全表扫描,需优化) |
| key | 实际使用的索引 | 是否命中预期索引 |
| key_len | 使用的索引长度 | 越短越好,判断联合索引使用情况 |
| rows | 预估扫描行数 | 越少越好 |
| Extra | 额外信息 | Using filesort(文件排序)、Using temporary(临时表)需要关注 |
type 类型效率对比:
flowchart LR
T1["system<br/>单表只有一行"] --> T2["const<br/>主键/唯一索引<br/>最多一行"]
T2 --> T3["eq_ref<br/>唯一索引<br/>关联查询"]
T3 --> T4["ref<br/>非唯一索引<br/>返回多行"]
T4 --> T5["range<br/>索引<br/>范围扫描"]
T5 --> T6["index<br/>索引全扫描<br/>遍历索引树"]
T6 --> T7["ALL<br/>全表扫描<br/>需优化"]
T7 -.-> N["出现 ALL(全表扫描)通常是重点优化对象<br/>建议添加索引或优化查询条件"]
上图展示了 EXPLAIN 中 type 字段的访问类型,从左到右效率递减:
system/const:最高效,主键或唯一索引查询,最多返回一行数据。eq_ref:唯一索引关联查询,每个索引键值对应一行。ref:非唯一索引查询,可能返回多行。range:索引范围扫描,如BETWEEN、>、<、IN等操作。index:遍历整个索引树,比ALL稍好(索引比数据文件小)。ALL:全表扫描,性能最差,需要重点关注优化。
三、索引优化:最常见的调优手段
1. 索引创建原则
-- 为高频查询条件创建索引 |
2. 索引失效场景
| 失效场景 | 失效写法 | 正确写法 / 说明 |
|---|---|---|
| 对索引列使用函数或计算 | WHERE YEAR(create_time) = 2024 | WHERE create_time >= ‘2024-01-01’ |
| 隐式类型转换(user_id 是 VARCHAR) | WHERE user_id = 123 | WHERE user_id = ‘123’ |
| LIKE 以通配符开头 | WHERE name LIKE ‘%张’ | WHERE name LIKE ‘张%’ |
| OR 条件中有一列无索引 | WHERE indexed_col = 1 OR unindexed_col = 2 | 会全表扫描,需为另一列建索引或改写 SQL |
| 联合索引不满足最左前缀 | INDEX(a, b, c),查询只用 b 或 c | 查询条件需包含最左列 a |
| !=、<>、NOT IN、IS NOT NULL | WHERE status != ‘DELETED’ | 部分场景可能不走索引,酌情改写 |
上图总结了索引失效的六大常见场景。每个场景都需要特别注意:
- 函数/计算:索引列经过函数处理后,B+ 树的有序性被破坏,无法利用索引。
- 隐式转换:MySQL 会将字符串转数字进行比较,相当于对列做了函数操作。
- LIKE 前缀通配:
%xxx无法利用 B+ 树的有序性,只能全扫描。 - OR 条件:只要有一个条件列没有索引,整体就会退化为全表扫描。
- 最左前缀:联合索引按照定义顺序构建,跳过前面的列无法利用索引。
- 否定条件:优化器可能认为返回数据量过大,直接全表扫描更高效。
四、SQL 语句优化技巧
1. 避免 SELECT *
-- ❌ 不推荐:查询所有列,增加 IO 和网络开销 |
2. 优化分页查询
-- ❌ 深分页性能差:需要扫描前 100000 条 |
3. 优化 COUNT 查询
-- ❌ COUNT(*) 扫描全表 |
4. 优化 ORDER BY
-- ❌ filesort:排序字段未走索引 |
五、表结构优化
1. 选择合适的数据类型
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 主键 ID | BIGINT 或 BIGINT UNSIGNED | 避免溢出,预留空间 |
| 状态字段 | TINYINT | 1 字节存储,配合枚举含义 |
| 字符串 | VARCHAR(N) | 变长存储,N 不宜过大 |
| 时间 | DATETIME 或 TIMESTAMP | 根据时区需求选择 |
| 大文本 | 独立表 + 外键 | 避免影响主表性能 |
2. 适度反范式设计
-- 范式设计:订单表只存 user_id,查询时 JOIN 用户表 |
六、高级调优工具
1. SHOW PROFILE(查询性能剖析)
-- 开启 profiling |
2. Performance Schema(MySQL 5.7+)
-- 查看哪些 SQL 执行时间最长 |
3. EXPLAIN ANALYZE(MySQL 8.0+)
-- 真实执行 SQL 并显示各步骤实际耗时 |
面试高频追问
- 联合索引的”最左前缀原则”是什么?为什么这样设计?
- 联合索引按照定义顺序构建 B+ 树,先按第一列排序,第一列相同再按第二列排序,以此类推。查询时必须从最左列开始匹配才能利用索引的有序性。
- 什么情况下应该创建索引?什么情况下不应该?
- 应创建:高频查询的 WHERE 条件、JOIN 关联列、ORDER BY/GROUP BY 列、区分度高(基数大)的字段。
- 不应创建:区分度低(如性别、状态只有几个值)、频繁更新的列、数据量小的表。
- 如何优化大批量数据导入?
- 临时关闭索引(
ALTER TABLE ... DISABLE KEYS)、使用批量 INSERT(一条语句插入多行)、关闭唯一性检查、增大innodb_buffer_pool_size。
- 临时关闭索引(
常见面试变体
- “EXPLAIN 执行计划中 type 字段有哪些值?分别代表什么?”
- “什么情况下索引会失效?举几个例子。”
- “如何优化 MySQL 的深分页问题?”
- “慢查询日志怎么开启?如何分析?”
记忆口诀
调优四步法:慢日志定位 → EXPLAIN 分析 → 索引优化 → 验证效果 索引失效六宗罪:函数计算、隐式转换、左模糊、OR 缺索引、最左缺失、否定条件 type 效率排序:system > const > eq_ref > ref > range > index > ALL
总结
MySQL SQL 调优的核心思路是:通过慢查询日志定位问题 SQL,使用 EXPLAIN 分析执行计划找出性能瓶颈(全表扫描、文件排序、临时表等),然后针对性地进行索引优化(遵循最左前缀、避免索引失效场景)、SQL 改写(避免 SELECT *、优化深分页)、表结构调整(选择合适字段类型、适度反范式)。调优是一个持续迭代的过程,需要结合业务场景和数据特点综合考量。
声明:本文转载自小哈的博客 《如何进行 SQL 调优?》,版权归原文作者所有。转载请保留本声明与原文链接。
评论
匿名评论隐私政策
WalineTwikoo
