MySQL 查询优化是一个系统性的过程,需要从索引设计、SQL 编写、数据库配置、硬件资源等多个维度进行综合调整。以下是详细的优化策略:
1. 索引优化
- 核心原则:80%的慢查询问题可通过索引解决。
- 添加必要索引:对
WHERE
、JOIN
、ORDER BY
、GROUP BY
涉及的字段创建索引。 - 联合索引顺序:遵循“最左前缀原则”,高频查询字段放在左侧。
- 覆盖索引:索引包含查询的所有字段(
SELECT
、WHERE
、JOIN
),避免回表。 - 示例:
SELECT id, name FROM users WHERE age > 20
→ 索引(age, id, name)
。 - 避免冗余索引:定期检查重复或未使用的索引(如
pt-duplicate-key-checker
工具)。 - 索引选择性:选择区分度高的字段(如唯一值多的字段)。
- 添加必要索引:对
2. SQL 语句优化
- 精简查询字段:避免
SELECT *
,只取必要数据。 - 避免全表扫描:禁止对索引字段使用函数或表达式(如
WHERE YEAR(create_time) = 2023
→ 改用范围查询)。 - 优化
OR
条件:用UNION ALL
替代可能导致索引失效的OR
。- 反例:
WHERE a = 1 OR b = 2
→ 正例:SELECT ... WHERE a = 1 UNION ALL SELECT ... WHERE b = 2
。
- 反例:
- 分页优化:大偏移量分页用
WHERE id > 10000 LIMIT 10
替代LIMIT 10000,10
。 - 子查询优化:将
IN
/EXISTS
子查询改写为JOIN
。 - 减少
JOIN
数据量:先过滤再关联,避免笛卡尔积爆炸。
3. 执行计划分析(EXPLAIN)
- 关键字段解读:
- type:访问类型(目标:
const
>ref
>range
>index
>ALL
)。 - key:实际使用的索引。
- rows:预估扫描行数。
- Extra:注意
Using filesort
(需优化排序)、Using temporary
(临时表)。
- type:访问类型(目标:
- 优化示例:
- 出现
Using filesort
→ 为ORDER BY
字段添加索引。 - 出现
Using where; Using index
→ 覆盖索引生效。
- 出现
4. 数据库设计优化
- 合理范式化:平衡范式与冗余,避免过度关联。
- 字段类型优化:
- 用
INT
而非VARCHAR
存储数值。 - 时间字段用
DATETIME
或TIMESTAMP
。 - 避免
NULL
,用默认值(如0
或空字符串)。
- 用
- 分区表:按时间或范围分区,减少单表数据量(如日志表)。
- 冷热数据分离:频繁访问的数据单独存放。
5. 配置与硬件优化
- 内存配置:
- 调整
innodb_buffer_pool_size
(通常设为物理内存的70%~80%)。 - 优化
sort_buffer_size
、join_buffer_size
(避免过高导致内存浪费)。
- 调整
- 日志配置:
- 关闭非必要日志(如
general_log
)。 - 调整
innodb_flush_log_at_trx_commit
(从 1 改为 2 或 0 可提升写入性能,但可能丢数据)。
- 关闭非必要日志(如
- 连接池管理:合理设置
max_connections
,避免连接风暴。 - 硬件升级:SSD 替换 HDD,增加内存容量。
6. 高级技巧
- 查询缓存:MySQL 8.0 已移除,建议用外部缓存(如 Redis)。
- 批量操作:
- 用
INSERT INTO ... VALUES (...), (...), ...
替代多条单行插入。 - 批量更新时,使用
CASE WHEN
或临时表。
- 用
- 异步处理:耗时操作(如统计)移至离线任务或消息队列。
- 读写分离:通过主从架构分散读压力。
7. 监控与工具
- 慢查询日志:开启
slow_query_log
,分析long_query_time
阈值以上的查询。 - 性能分析工具:
pt-query-digest
:分析慢查询日志。Percona Toolkit
:提供pt-index-usage
、pt-visual-explain
等工具。
- 实时监控:使用
SHOW PROCESSLIST
、Performance Schema
或第三方工具(如 Prometheus + Grafana)。
示例优化场景
问题SQL:
SELECT * FROM orders
WHERE status = 'pending'
ORDER BY create_time DESC
LIMIT 1000, 10;
优化步骤:
- 添加索引:
ALTER TABLE orders ADD INDEX idx_status_create_time (status, create_time DESC);
- 改写分页:记录上一页最大
create_time
,改用WHERE create_time < '2023-01-01' ... LIMIT 10
。 - 减少字段:替换
SELECT *
为具体字段。
通过以上方法,可显著提升查询性能。但需注意:优化需结合具体业务场景,避免过度设计。建议通过基准测试(如 sysbench
)验证优化效果。