IWA
2025-12-09
点 赞
0
热 度
1
评 论
0

Java 性能优化实战 第 7 篇:MySQL 性能优化必修课


MySQL 性能优化必修课:索引、慢 SQL、执行计划一次讲透

数据库性能,是后端系统 80% 性能瓶颈的源头。
你优化 JVM、优化 GC、优化线程池都可以,但只要 SQL 写得一般般 —— 系统一样会慢。

所有高并发架构师,都必须掌握 MySQL 性能优化的底层方法论

  • 如何设计高性能索引

  • 如何定位慢 SQL

  • 如何阅读与优化执行计划

  • 如何避免回表、避免 Using Filesort

  • 如何让 MySQL “走你预期的索引”

  • 如何写出高性能分页、统计、聚合语句

本篇我们直接从实战出发,讲开发者真正会遇到的问题。


一、MySQL 性能优化的三大方向

所有 MySQL 性能优化,都绕不开三件事:

  1. 减少扫描行数(最核心)

  2. 减少 I/O 次数(SSD/GCP 下仍然关键)

  3. 减少锁争用(高并发场景尤为重要)

而索引,就是减少扫描行数的最重要方式。


二、索引优化:高性能 SQL 的第一原则

1)为什么非要建索引?

因为 MySQL 的查询过程,本质上就是:

通过索引定位尽可能少的行 → 回表获取数据 → 返回结果

如果你没有索引,那 MySQL 只能全表扫描(Full Table Scan)。

想象一张 1000 万行的表,全表扫描就是血崩。


2)最常见的索引误区(你一定见过)

❌ 误区 1:给所有字段都加索引

过度索引会导致:

  • 写入变慢(所有索引都要同步维护)

  • 占用更多磁盘

  • 大多数索引根本不会被用到

❌ 误区 2:联合索引顺序随便写

(a, b, c)(c, b, a) 完全不是一回事!

MySQL 使用联合索引必须遵循最左前缀原则

❌ 误区 3:模糊查询用 %keyword% 期待走索引

%keyword% 永远不会走 B+ 树索引。
要优化全文检索请使用:

  • ES

  • MySQL FULLTEXT


3)高性能索引的黄金原则

原则 1:高区分度字段优先

区分度 = 去重后的数量 / 总行数
越接近 1 越好。

适合做索引的:

  • 手机号

  • 用户 ID

  • 订单号

不适合做索引的:

  • 性别(就 3 个值)

  • 状态(0/1)

原则 2:范围查询要放在索引最后

例如:

WHERE user_id = ? AND create_time BETWEEN ? AND ?

索引应建:

(user_id, create_time)

但不能反过来!

原则 3:联合索引覆盖越多字段越好

例如:

SELECT id, name, age FROM user WHERE name = ? AND age = ?

如果你建了:

(name, age, id)

就能做到 覆盖索引(Covering Index),不需要回表,大幅提升性能。


三、慢 SQL 定位套路:遇到性能问题的第一步

线上 90% 的性能问题都可以通过慢 SQL 日志定位。

1)开启慢查询日志

my.cnf

slow_query_log = 1
long_query_time = 1
log_queries_not_using_indexes = 1
  • 执行超过 1 秒的 SQL 会被记录

  • 没走索引的 SQL 也会被记录


2)如何分析慢查询日志?

用工具:

  • pt-query-digest(强烈推荐)

  • 阿里云 RDS 慢 SQL 控制台

  • 腾讯云 SQL 分析

这些工具会告诉你:

  • 哪些 SQL 最慢

  • 哪些 SQL 调用次数最多

  • 哪些 SQL 扫描行数最多

  • 哪些 SQL 占总耗时 80%

让你精准定位热点。


四、执行计划(EXPLAIN)阅读与优化

你必须学会看 EXPLAIN,因为它能告诉你:

  • 是否走索引

  • 走的是哪个索引

  • 扫描了多少行

  • 是否出现 Using Filesort / Using Temporary(很糟糕)

  • 是否产生回表

下面来拆解关键字段。


1)key 字段:实际使用的索引

如果这里是 NULL,说明 没有用索引

常见原因:

  • 索引不符合最左前缀原则

  • 模糊搜索 %xxx

  • 类型不一致(字符串 vs 数字)


2)rows 字段:扫描行数(越小越好)

例如:

rows = 1

非常好。

rows = 1,000,000

SQL 基本废了。


3)extra 字段:重点关注两个

Using Filesort

表示排序没有走索引排序,性能极差。

Using Temporary

表示产生临时表,一般出现在 GROUP BY 或复杂排序中。

出现这两个标签时,必须优化。


五、实战案例:SQL 为啥不走索引?怎么改?

案例 1:字段类型不一致

SQL:

SELECT * FROM user WHERE id = '123';

数据库:

id INT

类型不一致,会导致全表扫描。

解决:传参用整数。


案例 2:函数导致索引失效

WHERE DATE(create_time) = '2025-12-01'

因为 DATE(create_time) 对字段做了运算,索引失效。

优化:

WHERE create_time >= '2025-12-01 00:00:00'
  AND create_time <  '2025-12-02 00:00:00'

案例 3:OR 导致索引失效

WHERE user_id = ? OR phone = ?

OR 会让 MySQL 放弃索引。

优化方法:

  • 改成 union

  • 或者使用索引合并(MySQL 8.0 有时会自动触发)


六、高性能 SQL 写法模板(直接可用)

✅ 高性能分页

SELECT id, name
FROM user
WHERE id > ?
ORDER BY id
LIMIT 100;

比 OFFSET 快几十倍。


✅ 高性能计数

不要写:

SELECT COUNT(*) FROM big_table;

在 5000 万行大表会非常慢。

优化:

  • 维护冗余 count

  • 使用近似算法(HyperLogLog)

  • 分库分表时,分片并行 count


✅ 条件动态 SQL 优化

不要在 WHERE 条件写:

WHERE (name = ? OR ? IS NULL)

这会让索引失效。
正确做法是动态拼接 SQL。


七、总结

MySQL 优化的核心思想只有一句话:

让 MySQL 尽量扫描更少的行。

做到这一点,你的 SQL 性能自然提升一个维度。

本篇我们讲了:

  • 索引设计的黄金法则

  • 慢 SQL 定位方法

  • EXPLAIN 的关键字段

  • 常见执行计划陷阱

  • 真实 SQL 优化案例

  • 高性能 SQL 模板

下一篇我们继续讲:


《第 8 篇:Redis 性能优化与高并发热点缓存设计(上)》


用键盘敲击出的不只是字符,更是一段段生活的剪影、一个个心底的梦想。希望我的文字能像一束光,在您阅读的瞬间,照亮某个角落,带来一丝温暖与共鸣。

IWA

infp 调停者

具有版权性

请您在转载、复制时注明本文 作者、链接及内容来源信息。 若涉及转载第三方内容,还需一同注明。

具有时效性

文章目录

IWA的艺术编程,为您导航全站动态

43 文章数
9 分类数
19 评论数
34标签数

访问统计