我前几天排查一个后台报表慢查询,一条SQL里既有WHERE又有ORDER BY、GROUP BY、LIMIT,看起来每个关键词都认识,但执行计划就是不走索引,几百万行的表硬生生扫了十几秒。问题不在数据量,而在筛选、排序、分组、限制这几个动作之间的配合方式没想清楚。
这篇分享想把MySQL里最常用的四组查询动作拆开揉碎讲一遍:用WHERE筛选行,用ORDER BY排序,用GROUP BY分组,用LIMIT限制返回行数。它们单独用都不难,难的是组合在一起时怎么猜透MySQL的执行路径。文章会从执行顺序讲起,再到每类动作的常见坑、索引优化思路,最后附上实际业务里能直接套用的排查方法。刚学SQL的可以当语法进阶,写过一段时间但经常被慢查询困扰的,也能在里面找到几张能直接用的“药方”。
1. 先搞清SQL执行顺序,筛选才不容易写错
1.1 书写的SQL不是MySQL真正执行的顺序
大多数人写SQL的习惯是照模板:SELECT要哪些列,FROM哪张表,WHERE怎么过滤,GROUP BY怎么分组,ORDER BY怎么排,最后LIMIT分页。这个书写顺序没问题,但MySQL实际执行并不是从左往右按书写顺序跑的。
一条典型查询的真实执行顺序大致是这样的:先确定FROM后面那张表,把表数据取出来;接着执行WHERE,对每一行做条件判断,把不满足条件的行直接丢掉,此时大部分数据量已经被砍掉;然后把剩下数据按GROUP BY字段分组,分组后如果还有HAVING条件,再对组做一次过滤;之后才轮到SELECT去计算最终要展示的列和聚合值;最后再执行ORDER BY排序,排序完用LIMIT截取指定行数返回。
这个顺序解释了工作中常见的现象:在SELECT里给某个计算列起别名,却想在WHERE里直接用这个别名,结果MySQL报错“Unknown column”。原因很简单,WHERE执行时SELECT还没运行,别名根本不存在。反观ORDER BY就能用SELECT里的别名,因为排序发生在SELECT计算完成之后。实际编码中见过太多人踩这种错,把WHERE条件反复改了几遍才发现是顺序问题。
理解执行顺序还有一个价值:确认过滤时机。同样是筛选,发生在WHERE里的行级过滤越早越好,因为每早一步丢掉一批行,后面的分组、排序、分页成本都会成倍降低。判断自己的SQL过滤是不是足够早,最直接的办法是看EXPLAIN执行计划里的rows字段,如果预估扫描行数和表总行数差不多,说明过滤条件没有发挥应有作用。
1.2 拿着EXPLAIN看SQL,比凭空猜靠谱得多
碰到一条慢SQL,先别急着优化,先在SQL前面加一个EXPLAIN关键字跑一遍,MySQL会给出这条语句的执行计划,包括用了哪张表、预估扫描多少行、使用哪个索引、是否用到临时表和文件排序。EXPLAIN本身不执行真实查询,所以哪怕线上库也能放心查看。
执行计划重点看几个字段:
- type:访问类型,从好到差依次有system、const、eq_ref、ref、range、index、ALL。见到ALL就说明是全表扫描,这是最危险的信号。
- key:实际用到的索引名,如果是NULL,说明没走索引。
- rows:MySQL预估需要扫描的行数,这个数字越接近最终返回行数越好。
- Extra:如果出现Using filesort、Using temporary,就说明排序和分组没能利用索引,MySQL额外做了工作。
我在业务中大致的判断套路是:先看type是不是range以上,再看rows有没有被限定在一个可接受范围,最后看Extra有没有文件排序和临时表。这三个地方只要有一处不理想,就值得顺着执行顺序往下排查。工具给出来的信息比凭感觉优化要准确得多,SQL调优如果能坚持“先看执行计划再动手改”,基本能避开大部分无效努力。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 筛选过滤:WHERE的常见误区和高效写法
2.1 组合条件时的优先级陷阱
多条AND和OR混在一起写,是筛选阶段最容易出问题的地方。SQL里AND的优先级高于OR,也就是说,如果写的是:
sql复制WHERE status = 1 OR status = 2 AND amount > 100
MySQL会理解成status = 1,或者status = 2且amount > 100,而不是你脑子里以为的“(status = 1或status = 2)且amount > 100”。这个差异在业务上一旦理解错,查出来的数据就可能多出一大截,而且很难一眼发现。
解决办法就是加括号,把逻辑表示清楚:
sql复制WHERE (status = 1 OR status = 2) AND amount > 100
我见过一些老系统里的慢查询,排查半天发现不是索引问题,而是OR条件包没包括号导致结果集变大,间接让后续排序和分页也变慢了。条件逻
