4.2 执行计划解读与排错实录


4.2 执行计划解读与排错实录

本节摘要:执行计划是优化器写给你的判决书,也是慢查询排错的第一现场。本节先给出读计划的三步法与关键运算符词典,然后用一个完整的生产案件——报表查询从两秒劣化到四分钟——演示"背景、取证、干预、复盘、变式"的全程办案,最后交代查询提示与查询存储两个干预工具的正确分寸。

从最贵的算子下手,倒着读计划

图形化执行计划从左往右是数据流出的方向,但办案要从结果倒着回溯:先找成本占比最高的算子,再审它的行数与告警图标,最后把估计行数与实际行数对账。运算符不需要全背,这十来个覆盖九成案件:扫描与查找——扫描是把表或整个索引读一遍,查找是按 B 树精准下探;计划里"本该查找却在扫描",先查谓词是否可 SARG(对列做了函数包裹就废了)。三种连接——嵌套循环适合外表小内表有索引的精准打击,哈希适合大批量无序数据的一次性对撞,归并要求双方按连接键有序、适合超大且已排序的输入;优化器选错连接方式的常见原因是行数估计错得离谱。排序与溢写——排序需要内存授予,给不足就溢写到 tempdb,计划里出现黄色警告;这是报表查询慢的经典病因。键查找——非聚集索引找到行再回表,行数一大不如直接扫描,配合覆盖索引消灭。并行与交换——多核分工的通道算子,行数估计错会让并行变成负资产。

SET STATISTICS IO, TIME ON; -- 打开这条开关再跑慢查询: -- 表 '订单明细'。扫描计数 1,逻辑读 2854332 次,物理读 4120 次…… -- 逻辑读百万级起步的查询,优化方向几乎总是索引与写法。 SET STATISTICS IO, TIME OFF;

一桩四分钟报表的完整卷宗

背景:月末报表"订单毛利率汇总"原本两秒跑完,某月中旬起劣化到四分钟,业务方已两次升级工单。取证:开启实际执行计划与统计信息重跑。计划显示:订单明细表走了聚集索引扫描(估计行数与实际一致,都是五百万,合理);客户表走了嵌套循环的内侧索引查找——估计 1 行,实际 3000 行。对账发现第一个关键偏离:优化器以为客户筛选条件命中一个客户,实际命中三千个。于是循环被驱动了三百万次,每次都是一次回表随机读。为什么估计 1 行?打开统计信息的直方图,发现上次更新停留在两个月前——新客户的数据全不在直方图里,密度信息把选择性估到了天花板的乐观值。干预:先更新该表统计信息,重新编译后优化器改选哈希连接加并行扫描,耗时降到十一秒;再给订单明细表建按状态列的过滤索引,把十一秒压到两秒三。复盘:根因不是索引缺失而是统计过期,索引只是顺手的优化。查询上溯,找到统计过期的机制原因:该表每天凌晨全量重建(每次重建自动更新统计),但月中业务加了一个只插入的大批量任务,插入路径不触发统计更新阈值——旧规则的盲区。补了一条每周全量更新统计的作业收尾。变式:如果统计及时更新后依然慢,下一站查参数嗅探(对比不同参数下的计划形状);如果计划形状稳定但物理读高,转第 2 章的缓冲池取证;如果只有特定会话慢,查会话设置差异(隔离级别、兼容级别)。

干预工具箱:提示、指南与查询存储

有时你比优化器更懂业务,需要人工干预,工具按侵入度分三档。查询提示最直接:OPTION 里指定连接方式、最大并行度、重编译等。分寸是"提示是绷带不是假肢"——它锁死行为,数据分布变化后反而成为新瓶颈,只用于急救与已验证的特例。计划指南对改不了代码的场景有价值:不改应用 SQL,让服务器在匹配到特定语句时自动附加提示;代价是隐藏逻辑,必须留档。查询存储是现代答案:自动记录每条查询的历史计划与性能指标,遇到"新计划劣化"一键回退到旧计划(计划强制),等于给计划缓存装了黑匣子加回退按钮。2016 版之后的新实例没有理由不开它。

-- 查询存储三件套:开启、找劣化、强制回退 ALTER DATABASE 销售库 SET QUERY_STORE = ON; ALTER DATABASE 销售库 SET QUERY_STORE (OPERATION_MODE = READ_WRITE, MAX_STORAGE_SIZE_MB = 2048); SELECT q.query_id, r.runtime_stats_interval_id, r.avg_duration, r.count_executions FROM sys.query_store_runtime_stats r JOIN sys.query_store_plan p ON p.plan_id = r.plan_id JOIN sys.query_store_query q ON q.query_id = p.query_id ORDER BY r.avg_duration DESC; -- 从劣化时段找出 query_id 与 plan_id 后: EXEC sys.sp_query_store_force_plan @query_id = 42, @plan_id = 7; -- 一句话把好计划按回去,先止血再慢慢查统计信息。

💡 判断该不该用提示的一个土办法:把提示写在便签上贴工位,每季度盘点一次——还在贴着的每一条,都应该升级成查询存储强制计划、索引改造或统计信息修复这类"结构性方案",绷带不许穿成常服。

计划 XML 里的三处宝藏

图形界面之外,计划的可扩展标记语言(XML)版本藏着三处图形视图不易呈现的信息。第一处是缺失索引元素:它列出优化器"认为"缺的索引与预期收益——但切记它是当前查询视角的局部建议,照单全收会造成索引泛滥,正确用法是收集一段时间后按表归并、人工评审。第二处是参数编译值:计划 XML 里记录着编译时实际使用的参数值,参数嗅探案件里"哪个首见参数定了坏计划"一查便知,新版本还记录了运行时值,首见与运行值的对比直接揭示嗅探。第三处是告警节点:溢写、隐式转换、交换分区警告都以告警元素存在——隐式转换告警尤其值得关注,它意味着列类型与比较值类型不一致,索引因此失效,修复往往只是一次类型对齐。
把这三处纳入 4.2 的读计划三步法之后,形成第四步:翻 XML 对细节。前三步定性,第四步定案——尤其在"计划看起来合理但就是慢"的场景,告警元素几乎总能给出答案。

本节要点回顾

  • 三步读计划:最贵算子先行、估计实际对账、数据流向追问,顺序固定不乱翻;
  • 扫描变查找:先查谓词可 SARG 性(别用函数包裹索引列),再谈建索引;
  • 估计偏离是头号线索:本节案例估 1 实际 3000,顺藤摸到统计过期——治本是更新统计,建索引只是顺手;
  • 溢写警告:排序哈希溢写 tempdb 是报表慢的经典病因,扩大授予或减少数据量两条路;
  • 提示是绷带:急救可用,长期生效必须升级为结构性方案并留档;
  • 查询存储是黑匣子:开启后历史计划可查可回退,"一键按回好计划"是生产止血第一手段。

判决书会读、案件能破了。但优化器判决的证据——统计信息与基数估计——本身是怎么造出来的?下一节钻进这台"估数机器"的内部。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U