MySQL执行计划详解:从EXPLAIN字段到慢查询优化实战
说实话干了这么多年后端每天跟慢查询打交道最常用的工具其实就是EXPLAIN。不管你是刚入门MySQL的新手还是已经被线上问题磨了好几年的老油条执行计划都绕不开。它就像一个医生手里的CT机你写一条SQL过去它能直接告诉你MySQL准备怎么跑这条语句——走哪个索引、扫多少行、有没有临时表、要不要排序。这些信息拿到手慢查询优化就有了靶子不至于瞎猜。这篇东西我会讲得比较细把执行计划里每个关键字段、每个核心概念、我在实战中怎么用、遇到过哪些坑都摊开来说。内容偏向进阶适合已经会写SQL、想搞懂MySQL到底怎么工作的同学。1. 执行计划到底是什么以及什么时候该用很多同学一听“执行计划”就觉得高大上其实它就是MySQL优化器对一条SQL做的“执行方案”。这个方案里包含了访问表的方式、使用的索引、预估扫描的行数、表之间的连接顺序等等信息。你通过EXPLAIN关键字就能把这份方案调出来看。使用方式非常简单在SQL前面加上EXPLAIN就行EXPLAIN SELECT * FROM order_info WHERE user_id 10086;MySQL会把执行计划以表格形式返回每一行代表一个查询步骤。多表Join的SQL会返回多行每行对应一个表或者一个子查询。知道什么时候用比较关键。我个人的习惯是新上线的重要查询上线前先跑一次EXPLAIN确认没有全表扫描或者文件排序。线上慢查询日志里出现频率高的SQLCOPY出来先加EXPLAIN分析。别人写了SQL让你review别只看逻辑对不对直接看执行计划更靠谱。索引加了没效果用EXPLAIN看看到底走没走索引。所以它不是出了故障才翻出来的“救火工具”而是日常开发和review流程里的常规武器。2. 带着执行计划走一遍每个字段到底在说什么EXPLAIN的输出有十几个字段。刚开始接触的时候最容易犯的错就是只盯着type和rows看忽略了其他字段。但优化做到后面你会发现key_len、Extra、filtered这些字段才是真正的信息金矿。2.1 id和select_type看清查询结构id是查询的编号数字越大越先执行。对于简单查询id都是1。多表连接Join时如果都是普通连接id也相同此时MySQL会按照表的读取顺序来执行从上往下看就行。select_type描述的是一条SQL中每个SELECT部分的类型。常见的包括SIMPLE普通查询没有子查询和UNION。PRIMARY最外层的SELECT。SUBQUERY出现在WHERE或SELECT列表中的子查询非FROM子句。DERIVEDFROM子句中的子查询也就是衍生表。UNIONUNION操作中第二个及之后的SELECT。DEPENDENT SUBQUERY依赖外层查询结果的子查询这种通常性能不好。举个简单例子EXPLAIN SELECT * FROM user_info WHERE id IN (SELECT user_id FROM order_info WHERE status 1);外层是PRIMARY内层是SUBQUERY。如果看到DEPENDENT SUBQUERY就要高度警惕了很可能意味着每查一行外层数据内层子查询就执行一次这是典型的性能杀手。2.2 type这个字段基本决定了查询快慢type是执行计划里最值得关注的字段之一描述了MySQL如何访问表数据。它从左到右、从好到差大致是systemconsteq_refrefrangeindexALL实际业务中绝大多数情况下你能碰到的最好结果是ref能走到range已经可以接受最怕见到的是ALL也就是全表扫描。const通过主键或唯一索引最多只能匹配到一行。比如WHERE id 100MySQL直接把常量丢进索引一次定位。eq_ref多表Join时被驱动表通过主键或唯一索引进行匹配每行只匹配一条。ref通过普通二级索引进行等值匹配可能匹配到多行。这是比较常见的优秀访问类型。range通过索引进行范围查询比如、、BETWEEN、IN。index全索引扫描也就是遍历整个索引树比全表扫描好一些但也不快。ALL全表扫描意味着没有合适的索引可用。平时被慢查询折腾的SQLtype一般都是ALL。出现这种情况时第一反应应该是看一下WHERE条件里的列是不是缺索引或者索引没生效。2.3 possible_keys和key索引到底用没用上possible_keys列出了查询中可能用到的索引优化器候选的key是最终实际选择的索引。有些时候possible_keys不为空但key是NULL说明明明有索引优化器却没用。造成这种现象的原因大概有三种选择性太差。比如性别字段上建了索引但某个性别几乎占了一半数据MySQL觉得走索引还不如全表扫描。索引列上有计算或函数。比如WHERE DATE(create_time) 2024-01-01索引列套了函数直接废掉索引。隐式类型转换。比如字符串列user_no没加引号WHERE user_no 12345MySQL可能放弃走索引。遇到key和possible_keys不一致的情况别急着骂优化器先检查这三种情况大概率能找到问题。2.4 key_len索引长度里的门道key_len表示MySQL实际使用的索引字节数。这个字段很多人只看个大概但它是判断联合索引“用到了第几列”的关键依据。举个例子。假设有一张表CREATE TABLE order_info ( id BIGINT PRIMARY KEY, user_id INT, status TINYINT, create_time DATETIME, KEY idx_user_status (user_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;执行EXPLAIN SELECT * FROM order_info WHERE user_id 10086 AND status 1;key_len的值为5。为什么user_id是INT类型占4个字节加上可空标记占1个字节字段默认允许NULL时需要额外1字节合计5字节。这说明联合索引只用到了第一列user_id。如果key_len变成了6说明statusTINYINT占1字节可空标记1字节2字节也参与了匹配索引的两列都用上了。这个判断方式在处理联合索引时极其有用。你可以通过key_len精确知道SQL实际用了联合索引中的哪几列从而判断当前索引设计是否匹配查询。2.5 rows和filtered别全信但要注意趋势rows是MySQL估算的需要读取的行数filtered表示经过WHERE条件过滤后剩余行数的百分比。比如rows1000filtered10%意味着最终返回约100行。很多人过度依赖rows把它当成精确值。这里要提醒一下它是基于统计信息和索引基数的估算不是真实扫描行数。尤其当表数据分布不均、或者MySQL统计信息过期时rows的误差会很大。真正有价值的不是单看某一次rows数值而是看它的趋势。同样的SQL优化前rows是50万优化后变成500这就是非常直观的优化效果。2.6 Extra这里藏着MySQL的“潜台词”Extra可能是整个执行计划信息量最大的字段值得逐条琢磨。Using filesort需要额外排序操作。看到这个词基本可以认定为性能隐患。ORDER BY的列没有走索引MySQL只能把数据查出后在内存或磁盘上排序。如果是几万行还好几百上千万行就很要命。Using temporary使用了临时表。常出现在GROUP BY或DISTINCT子句执行过程中一般也意味着性能问题。Using index这是好事表示查询只访问索引不必回表覆盖索引。Using where表示在存储引擎层过滤后Server层还要再进行条件过滤。通常说明索引下推后还有WHERE条件落在普通列上。Using index condition索引下推ICP生效了MySQL会把部分WHERE条件下推到存储引擎层过滤减少了回表次数。Using join buffer被驱动表没走索引MySQL用join buffer做连接。会让性能直线下降。Extra的内容组合起来基本能还原MySQL执行时的“心路历程”。我在分析慢SQL时一半以上的判断结论都来自这个字段。3. 一个真实场景从执行计划到SQL重写光讲字段概念不够我拿一个自己处理过的经典场景来演示整个分析流程。假设业务上有张订单表数据量接近千万级别结构简化如下CREATE TABLE order_info ( id BIGINT NOT NULL AUTO_INCREMENT, order_no VARCHAR(64) NOT NULL, user_id BIGINT NOT NULL, status TINYINT NOT NULL, amount DECIMAL(10,2) NOT NULL, create_time DATETIME NOT NULL, PRIMARY KEY (id), KEY idx_order_no (order_no), KEY idx_user_id (user_id), KEY idx_create_time (create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;线上反馈某后台页面打开很慢定位到一条SQLSELECT * FROM order_info WHERE status 2 ORDER BY create_time DESC LIMIT 20;我第一时间执行计划拉出来看核心结果如下字段值typeALLpossible_keysNULLkeyNULLrows约932万ExtraUsing where; Using filesort出现typeALL、rows接近全表、Extra带Using filesort基本把慢查询的原因全占了。为什么status2没走索引因为status列本身没有独立索引。即便现建一个status单列索引以这个表的数据分布来看status2的订单可能占据了相当大的比例MySQL还是会选择全表扫描因为回表代价太大。真正的优化方向是让排序字段create_time参与索引匹配从而消除Using filesort。最终我选择建立联合索引ALTER TABLE order_info ADD INDEX idx_status_create_time (status, create_time);再跑执行计划结果变成字段值typerefkeyidx_status_create_timekey_len1rows约1.2万ExtraUsing index condition注意这里的key_len1因为status是TINYINT只占1字节。说明这次MySQL通过status等值匹配走了索引并且在索引内部就已经按create_time排好序了Extra里的Using filesort消失了。查询时间从原来的1.8秒降到40毫秒左右效果立竿见影。整个优化过程中执行计划最关键的信号就是那串ALL Using filesort。看到这两个组合脑子里就要立刻意识到索引缺失导致全表扫描排序也没走索引必须通过索引合并这两个消耗点。这个案例也说明一个道理很多时候不是SQL写错了而是索引设计没有跟上查询模式。执行计划就是用来验证这两者是否匹配的工具。4. 执行计划的进阶用法和排查技巧当基础字段都掌握了可以玩玩EXPLAIN的进阶姿势这些在定位复杂问题时非常有用。4.1 用FORMATJSON和FORMATTREE看更细的成本MySQL 8.0支持两种更直观的执行计划展示方式EXPLAIN FORMATJSON SELECT * FROM order_info WHERE user_id 10086; EXPLAIN FORMATTREE SELECT * FROM order_info WHERE user_id 10086;FORMATJSON会给出一个结构化的JSON输出里面包含query_cost信息可以直接看到优化器评估的IO成本和CPU成本。做方案对比时这个成本值可以作为量化参考。FORMATTREE的输出是树形结构能清楚看到表之间的连接顺序、哪个是驱动表、哪个是被驱动表。对于多表Join的场景这个格式对理解执行顺序帮助特别大。4.2 不要忽略EXPLAIN ANALYZEMySQL 8.0.18提供了一个更强大的工具——EXPLAIN ANALYZE。它不是估算是真的把SQL跑一遍输出实际执行时间和实际扫描行数EXPLAIN ANALYZE SELECT * FROM order_info WHERE user_id 10086;执行后输出类似这样- Filter: (order_info.user_id 10086) (actual time0.123..0.215 rows20 loops1) - Index lookup on order_info using idx_user_id (actual time0.098..0.190 rows20 loops1)这里的actual time和rows是真实的执行数据能帮你看清估算与实际之间的差距。线上排查时凡是遇到估算严重失真的情况用EXPLAIN ANALYZE一般能直接把真相拉出来。不过使用它要评估代价毕竟是真跑一遍SQL对于超大表的复杂查询建议在压测环境或低峰期使用。4.3 排查索引失效的几条自查路径执行计划里如果发现明明建了索引却没走我一般按顺序排查四件事第一看索引列上有没有函数或运算。WHERE SUBSTR(phone,1,3) 138这种写法直接废掉索引应该改成phone LIKE 138%。第二确认类型是否一致。字符串列查询不带引号数字列查询带了引号都会导致隐式类型转换让MySQL放弃候选索引。第三检查排序是否和索引顺序匹配。联合索引(a, b, c)你ORDER BY b是走不了索引的。如果ORDER BY a DESC, b ASC方向不一致也可能导致排序失效。第四看是不是统计信息太旧。表数据剧烈变化后如果还没有更新统计信息优化器可能做出错误的索引选择。运行ANALYZE TABLE 表名刷新一下统计信息再试往往能解决。4.4 关于深分页问题的额外提醒一条SQL如果Extra里只有Using wheretype也还不错但查询还是慢这时候大概率是LIMIT偏移量太大导致的。比如LIMIT 500000, 20MySQL要扫描前50万行再丢弃前面的代价极大。这类问题执行计划看不出来因为它展示的是访问路径不是行位移开销。解决手段是延迟关联或游标分页用ID做条件代替偏移量。这种场景下执行计划的作用变成了确认主查询走索引了但深分页的优化需要靠业务层SQL改造来完成。5. 常见问题速查表结合我日常回复同事和社区提问的经验把常见执行计划问题归纳成一张速查表症状可能原因解决思路typeALL无合适索引或选择性差加索引或改用覆盖索引ExtraUsing filesortORDER BY未走索引让排序列参与索引或改写SQLExtraUsing temporaryGROUP BY/DISTINCT未走索引优化索引或改写为JOINkeyNULL但有possible_keys函数/类型转换/选择性差消除索引列函数调整类型rows与预期差距极大统计信息过期ANALYZE TABLE刷新统计key_len短于预期联合索引未用全列审查查询条件与索引列顺序这张表几乎是日常DBA问题的浓缩版。遇到执行计划相关的疑惑对照表格基本能锁定大致方向。我自己在实际工作中还有一个习惯每定位到一个慢查询会把执行计划和最终优化方案记录在案定期复盘。时间久了哪些SQL写法容易踩坑、哪类索引设计存在误区心里都有谱。这算是在EXPLAIN之外更高一层的“执行计划思维”了。文章写到这里关于EXPLAIN的核心内容都覆盖了。如果你手头正好有慢SQL别犹豫先跑一下执行计划大部分问题自己就能看穿。优化的第一步永远是先看清MySQL到底在干什么。

相关新闻

CICD落地实践:Docker如何贯穿构建、交付与部署

CICD落地实践:Docker如何贯穿构建、交付与部署

先说个我观察到的现象:不管是自己搭流水线,还是用现成的CI工具,做持续集成、持续交付改造的团队,最后几乎都会绕到同一个点上——Docker。原因不复杂,CICD的核心诉求是快速、可靠、可重复,而Docker恰好在这…

2026/10/19 11:11:17 阅读更多 →
React前端实战:从模糊需求“rea”到组件化工程落地

React前端实战:从模糊需求“rea”到组件化工程落地

1. 需求拆解与方案选型1.1 接到一个只有"rea"的需求我拿到过形形色色的需求单,大部分好歹会写清楚"做一个登录页"、"调一下接口"、"页面白屏了帮忙看看"。但这个项目标题算极简到极致的:就三个字母,…

2026/10/19 11:11:17 阅读更多 →
蘑菇分类项目拆解:从CSV到模型对比的表格分类全流程模板

蘑菇分类项目拆解:从CSV到模型对比的表格分类全流程模板

简介:这份基于机器学习的蘑菇分类项目,面向计算机、数据科学、人工智能等相关专业学生及入门开发者,适用于课程设计、毕业设计、课程大作业或初期项目立项演示等场景,也适合希望从零上手分类任务的学习者。资源内包含完整可运行的…

2026/10/19 11:10:10 阅读更多 →

最新新闻

5 分钟上手 liboqs:后量子密码学 C 库的新手完整指南

5 分钟上手 liboqs:后量子密码学 C 库的新手完整指南

5 分钟上手 liboqs:后量子密码学 C 库的新手完整指南 【免费下载链接】liboqs C library for prototyping and experimenting with quantum-resistant cryptography 项目地址: https://gitcode.com/GitHub_Trending/li/liboqs liboqs 是一个用 C 编写的开源后…

2026/10/20 1:18:52 阅读更多 →
连接器与自动化控制厂商评估:三维度框架甄选长期合作伙伴

连接器与自动化控制厂商评估:三维度框架甄选长期合作伙伴

做设备集成和产线升级的工程师,应该都遇到过这种尴尬:聊连接器,一堆做线缆和接插件的厂商讲得头头是道,但一问到总线协议、运动控制和故障诊断,马上含糊其辞;反过来,找自动化整体方案&#xff0…

2026/10/20 1:18:52 阅读更多 →
php内存缓存插件yac的安装配置--平替apcu,多进程共享内存

php内存缓存插件yac的安装配置--平替apcu,多进程共享内存

1. 安装依赖与Yac扩展1.1 安装PHP开发工具及依赖Bashsudo apt update sudo apt install php7.4-dev php-pear pkg-config # 开发工具与PECL管理器1.2 通过PECL安装YacBashsudo pecl install yac安装过程需确认时,直接按回车使用默认配置。⚙️ 2. 配置PHP加载Yac扩…

2026/10/20 1:18:52 阅读更多 →
Tailwind组件库实战:从原子类到主题定制与避坑指南

Tailwind组件库实战:从原子类到主题定制与避坑指南

简介:这是一份 Tailwind CSS 6.23 版本的组件库资源包,面向 Web 前端开发者与 UI 工程师,以 utility-first 为核心,汇总了高频组件、实用类和官方 Tailwind UI 最佳实践,帮助快速搭建响应式、无障碍的现代界面&#xf…

2026/10/20 1:18:52 阅读更多 →
N_m3u8DL-RE流媒体下载工具实用指南:5分钟跑通 M3U8/MPD 点播与直播下载

N_m3u8DL-RE流媒体下载工具实用指南:5分钟跑通 M3U8/MPD 点播与直播下载

N_m3u8DL-RE流媒体下载工具实用指南:5分钟跑通 M3U8/MPD 点播与直播下载 【免费下载链接】N_m3u8DL-RE Cross-Platform, modern and powerful stream downloader for MPD/M3U8/ISM. English/简体中文/繁體中文. 项目地址: https://gitcode.com/GitHub_Trending/n…

2026/10/20 1:18:52 阅读更多 →
FLUX 3 Image Klein能商用吗?FLUX Kommunity License许可条款与合规红线实战指南

FLUX 3 Image Klein能商用吗?FLUX Kommunity License许可条款与合规红线实战指南

人工智能大模型基础模型媒体生成计算机视觉 【免费下载链接】flux-3-image-klein-base 项目地址: https://ai.gitcode.com/hf_mirrors/black-forest-labs/flux-3-image-klein-base 点击查看 免费下载 本文带你快速搞懂 FLUX 3 Image Klein 能商用吗 这个高频问题。…

2026/10/20 1:17:52 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →