MySQL性能优化大全

1. 先记住 10 条核心结论

  1. 不是"数据量小就一定快",2 万行如果执行计划错了,一样能慢到分钟级。
  1. 多表关联是否快,核心不在 JOIN 语法,而在驱动表和索引是否匹配。
  1. ORDER BY 想快,最好让排序列来自驱动表,并且能命中索引。
  1. LIMIT 想真正生效,必须尽量做到"先排序、先取前 N,再回表/再补关联"。
  1. 多列排序要配复合索引,单列索引通常不够。
  1. LEFT JOIN 后按右表字段排序,最容易退化成 Using filesort。
  1. WHERE、JOIN、ORDER BY 三者要一起设计索引,不能各管各的。
  1. 分页列表和 count(*) 应该分开优化,count 往往比 list 更容易被忽略。
  1. EXPLAIN 比"我觉得应该走索引"更重要。
  1. 优化 SQL,本质是在减少:扫描行数、排序行数、回表次数、临时表和 filesort。

2. 多表关联为什么会慢

典型慢点有 6 个:

  1. 驱动表选错
  • 你想按 storage_size 排,却从 dataset 起表,数据库就很可能先扫 dataset,再去关联 storage,最后统一排序。
  1. 排序发生在 JOIN 之后
  • 这时不是排 2 万条,而是排"JOIN 后的结果集",中间还可能有重复放大。
  1. 索引不匹配排序
  • 例如 SQL 是:

但你只有:

这通常不够。

  1. LEFT JOIN + 按右表字段排序
  • 优化器经常保守处理,容易 filesort。
  1. 一对多 JOIN 导致结果膨胀
  • 主表 2 万行不代表结果还是 2 万行。
  1. 条件、排序、关联列各自有索引,但不是同一个复合索引
  • MySQL 很多时候只能高效用一个主索引路径。

3. 先理解执行顺序

一条常见 SQL:

数据库不是"看起来这样写,就一定先按 b.score 排"。

它更可能做的是:

  1. 扫 a
  1. a.id = b.a_id 找 b
  1. 拼出结果集
  1. 对整个结果排序
  1. 取前 10

如果 b.score 的索引没法直接参与排序,就会看到:

  • Using filesort
  • Using temporary

这时候再小的数据量也能慢。


4. 多表 JOIN 的通用优化原则

4.1 先确定"主诉求"

SQL 只有三种主诉求:

  1. 先筛选主表
  • 比如"查某个用户的订单列表"
  1. 先按关联表排序
  • 比如"按存储大小排数据集"
  1. 先按关联表筛选
  • 比如"查某路径下的数据集"

不同诉求,驱动表通常不同。

4.2 谁负责排序,谁尽量做驱动表

比如:

  • 按 dataset.create_time 排序:通常 dataset 适合作驱动表
  • 按 dataset_storage.storage_size 排序:通常 dataset_storage 适合作驱动表

这是你这次从 1 分钟降到 1 秒的核心原因。

4.3 先取主键,再补字段

最稳的分页套路:

  1. 在最合适的表上筛选 + 排序 + limit
  1. 只取 id
  1. 再回表查详情
  1. 再补市场、权限、用户信息等

这比"大 SQL 一把梭"稳定得多。


5. 索引设计原则

5.1 复合索引顺序

通用顺序:

  1. 等值过滤列
  1. 排序列
  1. 次排序列 / 关联列
  1. 需要覆盖的少量字段

例如:

建议索引:

如果 SQL 是:

建议索引:

5.2 单列索引什么时候不够

SQL:

如果只有:

通常不如:

5.3 排序索引要看"完整 ORDER BY"

如果 SQL 是:

只给 storage_size 建索引,不一定能避免排序。

5.4 范围条件会截断索引能力

例如:

这时索引能否同时兼顾过滤和排序,要看列顺序和优化器选择,很多时候会顾此失彼。


6. 多表关联的常见场景与最佳写法

6.1 场景一:主表分页,关联表只展示,不参与排序

适合:

  • 列表按主表时间排序
  • 关联表只是展示补充字段

推荐:

优点:

  • 先把分页做小
  • 避免全量 join 再 limit

6.2 场景二:按关联表字段排序

适合:

  • 按 storage_size
  • 按 file_count
  • 按 score

推荐:

优点:

  • 排序发生在最应该排序的表上
  • 更容易用到排序索引

6.3 场景三:关联表既参与筛选又参与排序

推荐:

关键点:

  • 让排序列所在表主导排序
  • 过滤条件也尽量下推到子查询

7. LEFT JOIN、INNER JOIN、EXISTS 怎么选

7.1 INNER JOIN

适合:

  • 你只要有匹配记录的数据
  • 想让关联表参与筛选或排序
  • 更利于优化器选择高效计划

7.2 LEFT JOIN

适合:

  • 主表必须保留,即使关联表没有记录
  • 展示性补字段

不适合:

  • 高频排序还按右表列排
  • 很容易拖慢

7.3 EXISTS

适合权限判断、是否存在判断:

通常优于:

前提是有索引:


8. 多表分页最常见的 8 个坑

  1. 先 join 再 limit
  1. 先排序全量结果再 limit
  1. count 也 join 一堆无关表
  1. 一对多 join 后没去重
  1. 按右表字段排序却从左表起表
  1. 排序列没有和过滤列组成复合索引
  1. like '%xxx%' 让索引失效
  1. 以为"有索引就一定走索引"

9. 如何判断索引有没有真正生效

看 EXPLAIN 重点关注:

  • type
  • key
  • rows
  • Extra

重点解释:

  • Using filesort
  • 说明排序没完全走索引
  • Using temporary
  • 说明临时表参与了处理
  • ALL
  • 全表扫
  • ref / range / const
  • 一般比 ALL 好
  • rows
  • 预估扫描行数,太大就危险

你最想看到的是:

  • 排序子查询的驱动表就是排序列所在表
  • key 命中预期复合索引
  • Extra 没有明显 Using filesort

10. 典型索引模板

10.1 主表按时间分页

适用:

10.2 按存储大小排序

适用:

10.3 按文件数排序

10.4 按路径长度排序

10.5 权限过滤

10.6 按 owner + 时间分页


11. count() 的优化原则count 常见错误:

如果 count 根本不依赖 storage 或 market,就不要 join。正确思路:1. 能不 join 就不 join 2. 必须 join 一对多时,使用 count(distinct d.id) 3. 权限过滤优先用 exists 4. count 不要排序 5. count 不要 select 无关列---## 12. 排序和分页的高性能模板## 12.1 主表排序模板

select d.*, s.storage_size

from (

select id

from starlake_dataset

where delete_flag = 0

order by create_time desc, id desc

limit 0, 10

) p

inner join starlake_dataset d on d.id = p.id

left join starlake_dataset_storage s on s.dataset_id = d.id and s.delete_flag = 0;

12.2 关联表排序模板

select d.*, p.storage_size

from (

select s.dataset_id, s.storage_size

from starlake_dataset_storage s

where s.delete_flag = 0

order by s.storage_size desc, s.dataset_id desc

limit 0, 10

) p

inner join starlake_dataset d on d.id = p.dataset_id and d.delete_flag = 0;

12.3 带条件的关联表排序模板

select d.*, p.storage_size

from (

select s.dataset_id, s.storage_size

from starlake_dataset_storage s

inner join starlake_dataset d on d.id = s.dataset_id and d.delete_flag = 0

where s.delete_flag = 0

and d.owner = ?

and d.dataset_type = ?

order by s.storage_size desc, s.dataset_id desc

limit ?, ?

) p

inner join starlake_dataset d on d.id = p.dataset_id;

相关推荐
kaico20182 小时前
python操作数据库
开发语言·数据库·python
被摘下的星星2 小时前
MySQL 别名使用规则详解
数据库·mysql
墨着染霜华2 小时前
MySQL 重复数据删除语句
数据库·mysql
ego.iblacat2 小时前
PostgreSQL 数据库
数据库·postgresql
wgzrmlrm743 小时前
如何解决ORA-28040没有匹配的验证协议_sqlnet.ora版本兼容设置
jvm·数据库·python
一江寒逸3 小时前
零基础从入门到精通MySQL(附加篇):面试八股文全集
数据库·mysql·面试
数厘3 小时前
2.6MySQL库表操作指南(电商数据分析专用)
数据库·mysql·数据分析
需要点灵感3 小时前
SQL Server 存储过程语法整理
数据库·sql
刘~浪地球3 小时前
数据库与缓存--分库分表实战指南
网络·数据库·缓存