SQL Server 执行计划怎么看?快速定位 SQL 慢的根源

SQL Server 执行计划怎么看?快速定位 SQL 慢的根源

90% 的 SQL 性能问题,看一眼执行计划就能找到原因。本文不讲废话,直接告诉你怎么看、看哪里、哪些是红色警报


一、为什么一定要学会看执行计划?

SQL Server 真正执行的,不是你写的 SQL,而是优化器生成的执行计划

  • 同一条 SQL,数据量变化 → 执行计划可能改变

  • 索引变了 → 执行计划会变

  • 统计信息过期 → 执行计划会错

👉 不看执行计划,调优全靠猜。


二、如何打开执行计划?

✅ 方式一:SSMS(最常用)

在 SSMS 中:

  • 实际执行计划Ctrl + M

  • 预估执行计划Ctrl + L

建议:调优时一定用 实际执行计划,它包含真实行数、实际耗时。


✅ 方式二:SET 命令(脚本友好)

复制代码
SET STATISTICS PROFILE ON;
GO

SELECT ...
GO

SET STATISTICS PROFILE OFF;

✅ 方式三:Plan Cache(线上排查利器)

复制代码
SELECT TOP 10
    qs.total_elapsed_time / qs.execution_count AS avg_duration_ms,
    qs.execution_count,
    SUBSTRING(st.text, (qs.statement_start_offset/2)+1,
        ((CASE qs.statement_end_offset
            WHEN -1 THEN DATALENGTH(st.text)
            ELSE qs.statement_end_offset END
         - qs.statement_start_offset)/2)+1) AS sql_text,
    qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
ORDER BY avg_duration_ms DESC;

✅ 适合:线上慢 SQL 抓现行


三、执行计划怎么读?(从上往下,从右往左)

核心阅读顺序

复制代码
从右向左(数据来源)
↓
从上向下(执行顺序)

很多人一上来就盯着箭头粗细,其实算子类型比箭头更重要


四、必须警惕的 7 个"红色信号"

下面这些算子,出现一个就要警惕,出现两个基本可以确定慢的根源。


🚨 1. Table Scan(表扫描)------最致命

!Table Scan

含义:没有合适的索引,一行一行扫全表。

✅ 解决:

  • 在 WHERE / JOIN 字段上建索引

  • 检查索引是否被禁用

❌ 常见原因:

  • 没建索引

  • 索引列被函数包裹

  • 隐式转换(VARCHAR ↔ NVARCHAR)


🚨 2. Clustered Index Scan(聚集索引扫描)

虽然用了聚集索引,但本质是全表扫描

✅ 解决:

  • 优化 WHERE 条件

  • 考虑非聚集索引 + 覆盖索引


🚨 3. RID Lookup / Key Lookup(回表)

含义:非聚集索引没覆盖查询字段,需要回到表中拿数据。

复制代码
-- 原查询
SELECT Name, Age FROM Users WHERE Email = 'a@b.com';

-- 索引
CREATE INDEX IX_Email ON Users(Email); -- 缺 Name, Age

✅ 解决:

复制代码
CREATE INDEX IX_Email
ON Users(Email)
INCLUDE (Name, Age);

Lookup 次数 ≈ 返回行数,几万次 Lookup 就是性能灾难。


🚨 4. Sort(排序)------CPU 杀手

含义:内存中排序,内存不够就 Spill to TempDB。

✅ 解决:

  • 在 ORDER BY 字段上建索引

  • 减少排序字段数量

  • 确认是否真的需要排序

⚠️ 看到 Sort Warnings,说明已经用到了磁盘排序。


🚨 5. Hash Match(哈希匹配)

常见于:

  • 大表 JOIN

  • GROUP BY

✅ 特点:

  • 内存消耗大

  • 数据量大时容易溢出到 TempDB

✅ 优化方向:

  • 小表驱动大表

  • JOIN 字段建索引

  • 避免不必要的 GROUP BY


🚨 6. Nested Loops(嵌套循环)+ 高行数

外层循环一次,内层循环 N 次。

✅ 适合:小结果集

❌ 不适合:大表 JOIN

👉 如果 Nested Loops 的"外部输入"是大表,基本必慢。


🚨 7. Parallelism(并行)------双刃剑

并行本身不是坏事,但:

  • CXPACKET 等待过高

  • 并行开销占比过大

✅ 解决:

  • 优化索引,减少数据量

  • 调整 MAXDOP

  • 检查统计信息


五、箭头粗细 ≠ 一切,但这几个指标更关键

点击任意算子,看 属性窗口(F4)

指标 含义 危险信号
Estimated Number of Rows 预估行数 与 Actual 差距巨大
Actual Number of Rows 实际行数 远大于预估
Estimated Cost 预估成本 某一步占比 > 50%
Memory Grant 内存授予 过大或溢出
I/O Cost IO 成本 远高于 CPU Cost

预估 vs 实际行数差异巨大 = 统计信息过期


六、30 秒快速定位慢 SQL 的实战流程

Step 1:看最耗成本的算子

  • 哪个算子占比最高?

  • 是不是 Scan / Sort / Hash Match?


Step 2:看数据从哪里来

  • Table Scan?

  • Clustered Index Scan?

  • 有没有走索引?


Step 3:看是否有回表

  • Key Lookup / RID Lookup 是否存在?

  • 能否改成覆盖索引?


Step 4:检查排序和聚合

  • Sort 是否可避免?

  • GROUP BY 字段是否有索引?


Step 5:检查隐式转换

在执行计划中搜索:

复制代码
CONVERT_IMPLICIT

一旦出现,立刻检查字段类型是否一致。


七、一个完整的实战案例

原始 SQL

复制代码
SELECT o.OrderId, o.CreateTime, c.Name
FROM Orders o
JOIN Customers c ON o.CustomerId = c.Id
WHERE o.Status = 1
  AND o.CreateTime >= '2024-01-01'
ORDER BY o.CreateTime;

执行计划暴露的问题

  1. Clustered Index Scan on Orders

  2. Sort on CreateTime

  3. Key Lookup on Customers

优化后索引

复制代码
-- Orders 表
CREATE INDEX IX_Orders_Status_CreateTime
ON Orders(Status, CreateTime)
INCLUDE (OrderId, CustomerId);

-- Customers 表
CREATE INDEX IX_Customers_Id
ON Customers(Id)
INCLUDE (Name);

✅ 结果:

  • Scan → Seek

  • Sort 消失

  • Lookup 消失

  • 耗时从 2.3s → 12ms


八、常见执行计划误解澄清

误解 事实
索引 Scan 一定慢 小表、全表访问反而更快
Seek 一定快 高频率 Seek + Lookup 可能更慢
并行一定好 并行有协调成本
执行计划不变 参数嗅探会改变计划

九、进阶:用 DMV 验证你的判断

查看缺失索引建议

复制代码
SELECT *
FROM sys.dm_db_missing_index_details;

⚠️ 不要照单全收,要结合业务判断。


查看统计信息是否过期

复制代码
SELECT
    name,
    auto_created,
    stats_date(object_id, stats_id) AS last_updated
FROM sys.stats
WHERE object_id = OBJECT_ID('Orders');

十、一句话总结

执行计划不是让你"看懂",而是让你"发现问题"。

记住这个公式:

慢 SQL = 扫描 + 回表 + 排序 + 错误的 JOIN 策略

相关推荐
专注API从业者1 小时前
告别人工盯品!Open Claw 搭建京东商品全自动监控与数据分析系统(附完整可运行代码)
大数据·数据库·数据分析
雪之下雪乃的代码日记1 小时前
Python快速入门(Java开发者版)
java·开发语言·笔记·python
敲个大西瓜2 小时前
Springboot核心面试题
java·spring boot·后端
器灵科技2 小时前
Seedance2.5 VS MiniMax H3 同日上线:AI短剧创作者该怎么选?
java·人工智能·阿里云·prompt·aigc
xbgRS2 小时前
Mybatis源码-核心流程及核心类
java·mybatis
zzh___zzh2 小时前
Java Stream API 常用方法笔记
java·笔记
萌动的小火苗3 小时前
1、python基础面试题
java·开发语言·python
molaoye3 小时前
win10下安装MySQL 8.0.*实录
数据库·mysql
麻雀聊技术3 小时前
一重启 AI 就失忆?用 Spring AI + PostgreSQL 3步搞定“长期记忆”持久化(附完整代码)
java·spring·openai