智睿享
白蓝主题五 · 清爽阅读
首页  > 日常经验

SQL查询执行计划查看:从慢查询到性能优化的实战经验

前几天帮同事排查一个系统卡顿的问题,发现根源出在一条SQL上。页面加载要十几秒,一查日志,原来是数据库拖了后腿。拿到这条SQL后,第一反应不是改写,也不是加索引,而是先看它的执行计划。

为什么执行计划这么重要?

很多人遇到慢查询,第一反应是“加个索引试试”。可现实是,有时候加了索引也没用,甚至更慢。原因在于你不知道数据库是怎么执行这条SQL的。就像开车导航,如果不看路线图,光猛踩油门,只会越走越偏。

执行计划就是数据库的“行车路线图”,它告诉你:表是怎么扫描的、走了哪个索引、有没有做嵌套循环、是否触发了临时表或文件排序。

怎么看执行计划?用EXPLAIN就够了

在MySQL里,只需要在SQL前面加个 EXPLAIN,就能看到执行计划。比如:

EXPLAIN SELECT * FROM users WHERE email = 'john@example.com';

执行后你会看到一堆字段,其中几个关键的得盯住:

  • type:访问类型,从 const 到 ALL,越靠前越好。ALL 意味着全表扫描,基本可以判定有问题。
  • key:实际使用的索引。如果这里是 NULL,那你写的索引可能没生效。
  • rows:预估扫描行数。如果是几万甚至几十万,就得警惕了。
  • Extra:额外信息,出现 Using filesort 或 Using temporary 基本意味着性能隐患。

一个真实案例:明明有索引却没走

有次写了个查询:

SELECT * FROM orders WHERE YEAR(create_time) = 2024;

虽然 create_time 字段上有索引,但 EXPLAIN 显示 type=ALL,key=NULL。问题出在函数 YEAR() 上——对字段使用函数会导致索引失效。

改成范围查询就解决了:

SELECT * FROM orders WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';

再看执行计划,type 变成了 range,key 也用上了,rows 从 50 万降到几千。

别只盯着单条SQL,联合查询更要看执行顺序

多表关联时,EXPLAIN 的输出顺序其实是执行顺序。数据库会决定哪张表当驱动表。通常小结果集的表应该放在前面,否则容易引发笛卡尔积。

比如下面这条:

EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id WHERE u.status = 1;

如果 users 表中 status=1 的数据很少,那它就应该当驱动表。但如果执行计划显示 orders 是第一行,那就说明器可能判断失误,这时候可以用 STRAIGHT_JOIN 强制顺序,或者加 Hint 提示。

不同数据库语法略有差异

MySQL 用 EXPLAIN,PostgreSQL 也一样。Oracle 要用 EXPLAIN PLAN FOR,然后查 plan_table。SQL Server 则支持 SET SHOWPLAN_ALL ON 或图形化执行计划。虽然写法不同,但核心思路一致:看扫描方式、看索引使用、看数据量级。

养成习惯:上线前先过一遍执行计划

现在我写完一条复杂SQL,第一件事就是 EXPLAIN 一下。哪怕看起来很简单,也可能因为数据分布、统计信息不准导致执行路径异常。特别是在分页查询、模糊匹配、IN 子句这些场景下,执行计划很容易“翻车”。

有一次线上加了个 IN 查询,本地测试没问题,结果上线后数据库CPU直接拉满。一查执行计划,发现 IN 里的子查询被展开成多个全表扫描。换成临时表+JOIN 后,压力立马下来了。

执行计划不是高级功能,它是每个写SQL的人都该掌握的基本功。别等系统崩了才想起看它。