执行计划一夜之间变了?别查代码了,是统计信息在“说谎“
大家好我是小耶写功课只是为了我踩过的坑你们别再踩了有个经典的凌晨惊魂场景某条核心SQL跑了半年都没问题每天几十万次执行响应时间稳定在5毫秒以内。某天凌晨三点监控告警疯狂弹窗——这条SQL突然飙到5秒CPU打满整个系统雪崩。DBA赶到现场第一反应是看代码有没有人改。没有。看索引有没有人删。没有。看数据量有没有暴涨。也没有。最后查下来原因让人无语统计信息过期了。数据库优化器拿着一份过时的情报给这条SQL选了一条错误的执行计划。今天把执行计划突变的底层原理、排查手段和预防机制一次讲清楚。一、先搞懂几个概念执行计划Execution Plan数据库执行一条SQL的具体步骤。先走哪个索引、先JOIN哪张表、用什么JOIN方式Nested Loop、Hash Join、Merge Join这些决策组合起来就是执行计划。优化器Optimizer数据库里的决策引擎负责为每条SQL选择最优的执行计划。它不跑SQL只猜哪种执行方式最快。统计信息Statistics优化器做决策的依据。包括表的总行数、每列的数据分布最大值、最小值、NULL占比、直方图、索引的选择性不同值的数量等。本质上就是优化器眼中的数据库快照。基数估计Cardinality Estimation优化器预估每一步会返回多少行数据。预估准了执行计划就优预估偏了就可能选错索引、选错JOIN顺序。CBOCost-Based Optimizer基于成本的优化器。优化器根据统计信息计算每种执行计划的成本CPU消耗、IO次数、内存占用选成本最低的那个。理解了这些概念就能回答一个核心问题为什么执行计划会突然变二、执行计划为什么会背叛你统计信息过期优化器拿到的是过期情报这是最常见的原因。统计信息不是实时更新的大多数数据库是定期收集或手动触发。假设你有一张订单表平时100万行统计信息也是按这个量级收集的。某天大促数据量涨到500万但统计信息还没更新。优化器依然认为表里只有100万行——于是选择了全表扫描因为它觉得100万行全表扫比走索引快。实际上500万行全表扫描直接卡死。统计信息 ≠ 实时数据它是一份延迟的快照。数据倾斜平均值骗了优化器即使统计信息是新的也可能因为数据分布不均匀而误导优化器。比如一个订单表的status列99%的数据是COMPLETED1%是PENDING。如果统计信息只记录了平均分布没有收集直方图优化器会认为每个状态的占比差不多。当你查询status COMPLETED时优化器预估返回1000行总行数10万的1/100实际返回99000行——走索引反而比全表扫慢几十倍。索引变化新增索引不一定是好事开发同学看到慢SQL第一反应是加索引。加完索引后统计信息更新优化器重新评估所有可用索引可能选出一个更差的执行计划。加索引 ≠ SQL变快它只是给优化器多了一个选择而这个选择可能是错的。参数变更看似无关的配置调整optimizer_mode从ALL_ROWS改为FIRST_ROWSoptimizer_features_enable版本升级后行为变化statistics_level从TYPICAL改为BASIC停止收集部分统计信息这些参数调整不会立刻生效但下一次硬解析时优化器的决策逻辑可能完全改变。三、执行计划突变的排查步骤第一步确认是不是执行计划变了-- Oracle SELECT * FROM v$sql_plan WHERE sql_id your_sql_id; -- MySQL (8.0) EXPLAIN FORMATTREE SELECT ...; -- PostgreSQL EXPLAIN (ANALYZE, BUFFERS) SELECT ...; -- 对比历史执行计划 -- Oracle: DBMS_XPLAN.DISPLAY_AWR(your_sql_id)重点对比访问路径全表扫 vs 索引扫描、JOIN顺序、JOIN方式、预估行数 vs 实际行数。第二步检查统计信息是否过期-- Oracle查看表的统计信息收集时间 SELECT table_name, last_analyzed, num_rows, blocks FROM user_tables WHERE table_name YOUR_TABLE; -- MySQL查看InnoDB表统计信息 SHOW TABLE STATUS LIKE your_table; -- PostgreSQL查看统计信息 SELECT last_analyze, last_autoanalyze, n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname your_table;如果last_analyzed是几天甚至几周前而这段时间数据变化超过10%基本可以判定统计信息过期。第三步对比预估行数和实际行数这是判断优化器是否误判的关键指标。-- 执行SQL时开启实际执行统计 -- Oracle: EXPLAIN PLAN FOR ... 然后 SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); -- MySQL: EXPLAIN ANALYZE SELECT ...; -- PostgreSQL: EXPLAIN ANALYZE SELECT ...;如果某一步的预估行数estimated rows和实际行数actual rows相差10倍以上说明基数估计严重失真执行计划很可能选错了。第四步检查是否有绑定变量窥探问题绑定变量第一次执行时优化器会窥探变量值来生成执行计划。后续执行直接复用这个计划即使变量值的数据分布差异很大。比如第一次传的是status PENDING只有100行优化器选了索引扫描。后面传的是status COMPLETED99000行还是走索引——但全表扫描反而更快。四、预防执行计划突变的4种手段手段一合理设置统计信息收集策略不要完全依赖自动收集根据业务特点定制策略适用场景收集频率自动收集 默认阈值数据变化平稳的普通表系统自动触发手动定时收集数据批量导入/删除的表每天凌晨或批量操作后锁定统计信息历史归档表数据不变化收集一次后锁定收集直方图数据分布严重倾斜的列按需收集-- Oracle: 手动收集统计信息含直方图 EXEC DBMS_STATS.GATHER_TABLE_STATS(SCHEMA, TABLE_NAME, method_opt FOR COLUMNS SIZE AUTO skewed_column); -- MySQL: 手动分析表 ANALYZE TABLE your_table; -- PostgreSQL: 手动分析 ANALYZE your_table;手段二使用执行计划基线Plan BaselineOracle提供了SQL Plan ManagementSPM可以把好的执行计划锁定下来即使统计信息变化也不让优化器切换到更差的计划。-- Oracle: 创建执行计划基线 DECLARE l_plans_loaded PLS_INTEGER; BEGIN l_plans_loaded : DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE( sql_id your_sql_id); END;金仓数据库也提供了类似的执行计划管理能力。通过DBMS_SPM兼容包可以将经过验证的优秀执行计划固定下来避免因统计信息变化导致的性能波动。同时支持执行计划演化evolve在确认新计划更优后才自动切换。手段三SQL Profile / OutlineSQL Profile是优化器的纠正器。当发现某条SQL的执行计划不理想时可以创建一个SQL Profile告诉优化器这条SQL按这个方式执行。-- Oracle: 使用SQL TUNING ADVISOR DECLARE l_tuning_task VARCHAR2(30); BEGIN l_tuning_task : DBMS_SQLTUNE.CREATE_TUNING_TASK(sql_id your_sql_id); DBMS_SQLTUNE.EXECUTE_TUNING_TASK(l_tuning_task); DBMS_SQLTUNE.ACCEPT_SQL_PROFILE(task_name l_tuning_task); END;手段四监控统计信息变化建立监控机制在统计信息过期前主动预警-- 找出统计信息超过7天未更新的表 SELECT table_name, last_analyzed, ROUND((SYSDATE - last_analyzed), 1) as days_since_analyze FROM user_tables WHERE last_analyzed SYSDATE - 7 ORDER BY last_analyzed;建议在监控系统中加入以下告警项核心表统计信息超过X天未更新单表数据变化量超过上次统计的10%执行计划发生变更对比AWR报告五、总结执行计划突变的本质是优化器拿着一份过时的地图给你指了一条错误的路。代码没改、索引没动SQL突然变慢——不要急着翻代码先查统计信息。排查执行计划问题按这个顺序来对比执行计划确认是不是执行计划变了不是SQL本身的问题检查统计信息last_analyzed多久了数据变化量超过10%了吗预估 vs 实际基数估计偏差超过10倍优化器就失明了绑定变量第一次执行的变量值可能不适合后续的变量值预防胜于治疗统计信息收集策略 执行计划基线 变化监控三管齐下让慢SQL扼杀在摇篮里。小耶在手SQL不愁。还有什么想了解的欢迎留言小耶一定知无不言言无不尽……我们下次见~

相关新闻

大模型采样参数调参全解:Temperature、Top_k、Top_p 实操指南

大模型采样参数调参全解:Temperature、Top_k、Top_p 实操指南

不少开发者在调用大模型 API 时,只会配置接口地址、模型名称与密钥,面对 Temperature、Top_p、Top_k 三大采样参数一头雾水。调参全靠盲目试错:想要严谨专业的报告,AI 却天马行空胡乱编造;需要创意故事、营销文案&…

2026/9/21 9:56:27 阅读更多 →
从流程图到可执行代码:基于Activiti的流程引擎完整生命周期解析

从流程图到可执行代码:基于Activiti的流程引擎完整生命周期解析

1. 项目概述:从流程图到可执行代码的旅程在任何一个涉及审批、流转或自动化处理的软件项目中,流程引擎都是核心的“中枢神经系统”。我们经常在需求文档里看到用BPMN(业务流程模型与标记法)画的流程图,那些圆角矩形、菱…

2026/9/22 23:13:09 阅读更多 →
从初级到高级,网络安全攻防工程师AD认证三级课程体系深度解析

从初级到高级,网络安全攻防工程师AD认证三级课程体系深度解析

网络安全攻防工程师(AD)认证设置初级、中级、高级三个等级,形成无断点的职业能力成长路径。每个级别都有明确的培养目标和课程体系,学员可以根据自身基础选择起点,逐步进阶。本文将深度解析AD认证三级课程体系的详细内…

2026/9/21 15:13:39 阅读更多 →

最新新闻

EverOS 记忆工作原理:Markdown 为源、SQLite 与 LanceDB 为派生索引的分层存储与同步管线

EverOS 记忆工作原理:Markdown 为源、SQLite 与 LanceDB 为派生索引的分层存储与同步管线

EverOS 记忆工作原理:Markdown 为源、SQLite 与 LanceDB 为派生索引的分层存储与同步管线 【免费下载链接】EverOS One portable memory layer for every AI agent: local-first, Markdown-native, user-owned, and self-evolving across apps, tools, and workflow…

2026/9/23 23:02:14 阅读更多 →
LSTM时间序列预测实战:Python源码解析与调参避坑指南

LSTM时间序列预测实战:Python源码解析与调参避坑指南

简介:基于LSTM的时间序列分析预测Python源码,面向数据科学、人工智能方向的学习者与开发者。项目以空气污染数据为例,完整覆盖数据加载与归一化、LSTM模型构建(基于Keras/TensorFlow)、模型训练、评估与未来值预测等环…

2026/9/23 23:02:14 阅读更多 →
长尾商品销量预测:基于DNN的时序预测与特征工程实战

长尾商品销量预测:基于DNN的时序预测与特征工程实战

简介:面向供应链备货中的长尾商品销量预测难题,这份基于TensorFlow 1.13编写的DNN项目源码,提供了7天、30天和60天三档预测的实现思路,适合有一定Python基础、希望借助低阶API掌握模型训练与部署的开发者。压缩包共6个文件&#x…

2026/9/23 23:02:14 阅读更多 →
ALOHA协议吞吐率仿真与优化:从18.4%到时隙ALOHA的工程实践

ALOHA协议吞吐率仿真与优化:从18.4%到时隙ALOHA的工程实践

简介:这份资源围绕ALOHA与时隙ALOHA多址接入协议的性能仿真展开,面向无线通信、卫星通信及局域网方向的学习者与研究人员,帮助理解时隙划分、随机发送、碰撞检测与捕获效应等核心机制。压缩包共2个文件,均为m脚本文件,…

2026/9/23 23:02:14 阅读更多 →
C# UHF RFID上位机开发:从DEMO到实战的串口通信与EPC解析

C# UHF RFID上位机开发:从DEMO到实战的串口通信与EPC解析

简介:这份资源是面向C#开发者与RFID入门者的UHF RFID阅读器演示工程,围绕UHFReader09型号设备,展示如何在.NET环境下完成标签读取、写入、解码及阅读器参数控制等核心操作。压缩包共52个文件、约660KB,以cs源代码为主体&#xff0…

2026/9/23 23:02:14 阅读更多 →
基于Python的淘宝京东商品评论爬虫与情感分析系统实战解析

基于Python的淘宝京东商品评论爬虫与情感分析系统实战解析

简介:这是一份基于Python开发、面向毕业设计与期末大作业场景的商品评价系统完整资源,覆盖淘宝、京东商品评论爬虫采集与情感分析全流程。系统整合了Python爬虫、数据处理及LSTM等情感分析模型,适合需要完成电商评论分析类项目的计算机专业学…

2026/9/23 23:01:12 阅读更多 →

日新闻

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A…

2026/9/23 0:00:23 阅读更多 →
2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我 刚把开发环境的显示器从1080P换到2K,跑老项目直接报错,版本升级后 API…

2026/9/23 0:01:25 阅读更多 →
3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点 官方文档翻了三遍还是云里雾里?别急,美眉图在实战项目中常被用来做数据可视化,但它的原理比你想的简单。今天咱们直接上手,用一个完整的小项目把美眉图跑通,不再死磕那些冗长的理论说明。…

2026/9/23 0:01:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →