MySQL ORDER BY LIMIT 为什么有时不走过滤索引:排序索引、filesort 和核对方法
订单列表只显示最近 20 条,接口却在高峰时偶尔变慢。EXPLAIN 里还出现了一个反直觉的现象:表上明明有按状态建的索引,优化器却选择按主键顺序读取,再边读边过滤。很多人看到这里就想强制索引,结果把一次局部波动改成了长期负担。先把问题拆开:这条 SQL 同时在争取更少的过滤行和更快的排序输出,LIMIT 会改变两者的成本权重。
实践要点
ORDER BY ... LIMIT不能只看 WHERE 条件;排序索引与过滤索引可能给出不同的低成本路径。- 复合索引应围绕真实的等值条件和稳定排序字段设计,别为了一个列表页堆一串无用列。
EXPLAIN用来读候选路径,受控的EXPLAIN ANALYZE才能看到实际读行和耗时;两者都要结合数据分布判断。
LIMIT 20 让“先过滤”与“先排序”发生竞争
看一个常见订单页:用户只看已支付订单,并要求按订单号倒序返回 20 条。过滤索引可以快速定位 status = 'PAID' 的范围,但若它无法同时满足 ORDER BY id DESC,服务器还要再排一次序。另一条路是沿着主键倒序读取,遇到已支付记录就收下,够 20 条就停;如果最近订单大多已支付,这条路可能更省。
反过来,若已支付记录在最近订单中很稀疏,按主键一路回扫会读过很多不匹配行,过滤索引加排序反而可能更合适。MySQL 官方文档说明,带 LIMIT 的排序查询会影响索引选择;可用有序索引时很快,若必须 filesort,则要先选出并排序匹配行。这里别急着给某一种路径贴“永远更快”的标签。

先用 EXPLAIN 看清它准备怎样读取
先把线上真实条件缩成可复现的查询,再看 key、rows 和 Extra。下面的 SQL 只作为示意,订单状态、时间窗口和排序字段应替换成自己的业务条件。
EXPLAIN SELECT id, status, created_at FROM orders WHERE status = 'PAID' ORDER BY id DESC LIMIT 20;
key表示本次路径选择的索引,不等于“表上最好的索引”。rows是优化器预计要检查的行数;对 InnoDB 来说它是估算值,不能把它当成精确计数。Extra出现Using filesort说明排序没有直接由当前读取顺序解决,但它本身不是判定慢查询的结论。
把两条候选 SQL 的 key、rows、返回行数和响应时延放在一起看,才能知道瓶颈是筛选稀疏、排序量过大,还是回表读取的字段太宽。只盯着一个 Using filesort,很容易把原本读取很少的数据路径误判掉。
复合索引要同时服务筛选与稳定排序
如果产品定义是“某状态下按创建时间倒序,订单号做同时间的稳定次序”,索引和 SQL 的排序表达就应该一致。等值过滤列通常放在前面,随后才是排序列;把唯一的 id 放进排序,可以避免同一时间值下翻页顺序漂移。
CREATE INDEX idx_orders_status_created_id ON orders (status, created_at, id); SELECT id, created_at FROM orders WHERE status = 'PAID' ORDER BY created_at DESC, id DESC LIMIT 20;
这不是要求每个列表页都新增索引。它是一个待核对的候选结构:先确认 WHERE 与 ORDER BY 是否真是这个组合,再看写入成本、索引体积和已有索引能否复用。MySQL 官方文档也提醒,索引不是越多越好;每个额外索引都会占用空间,并增加插入、更新和删除的维护成本。
上面的查询只取 id 与 created_at。若结果所需字段都在同一个索引里,就有机会作为覆盖索引读取,减少回到表数据页的需要。把 SELECT * 原样搬进来通常就失去这个条件,因此应先确认列表页真正渲染了哪些字段。

两个反例比“加索引”更值得先看
第一个反例是排序方向或列顺序和索引不一致。例如业务按 created_at DESC, id ASC 排序,索引却按另一种方向与顺序建立;不能凭肉眼认定它一定能复用排序。第二个反例是过滤条件本身范围很宽,或者一次查询需要取大量不在索引中的字段。此时即使索引名称看起来匹配,回表和读取量也可能抵消优势。
还有一种常见误区是只为一个低频后台页面加很宽的覆盖索引。读侧少了一点回表,写侧却让每次状态更新都多维护一份大索引。架构上的取舍不在“覆盖索引好不好”,而在这个读取模式是否足够高频、字段是否足够稳定、写入成本是否能接受。
把 prefer_ordering_index 当成对比开关,不当成长期答案
MySQL 针对带 ORDER BY 和 LIMIT 的查询会偏向考虑有序索引。官方文档提供了 prefer_ordering_index 开关,便于比较另一条候选路径。更稳妥的做法是在隔离的会话或测试环境里做一次对比,不要直接把全局默认改掉。
SET SESSION optimizer_switch = 'prefer_ordering_index=off'; EXPLAIN SELECT id, status, created_at FROM orders WHERE status = 'PAID' ORDER BY id DESC LIMIT 20;
这一步的价值是帮助理解“排序优先”和“过滤优先”各自会选什么,不是替业务定结论。需要实际度量时,EXPLAIN ANALYZE 会真正运行 SELECT 并报告测量数据,应该只对受控查询使用,避免把昂贵的生产读请求额外再跑一遍。
上线前按这张表收口
| 核对点 | 应看到的结果 | 异常时优先检查 |
|---|---|---|
| 排序稳定性 | ORDER BY 包含能打破并列的列 | 同时间值或同状态值下是否有跳页、重复 |
| 索引形状 | 等值条件与排序列和业务 SQL 对齐 | 列顺序、方向、范围条件与现有索引 |
| 读取量 | 实际检查行数与返回 20 行的目标相称 | 过滤稀疏度、filesort、回表字段 |
| 写入代价 | 新增索引没有明显拉高写入延迟 | 索引宽度、更新频率、重复索引 |
相关问题
Using filesort 一出现就必须优化吗?
不一定。它说明当前排序没有直接按索引顺序完成,但还要看匹配行数、排序量和实际时延。少量数据上的 filesort 可能比沿着不合适的排序索引扫很多行更省。
为什么 ORDER BY 要加 id?
当主要排序列存在相同值时,加唯一列可以让返回顺序稳定。MySQL 文档也指出,只按非唯一排序列配合 LIMIT 时,并列行的相对次序不保证固定。
覆盖索引是不是越宽越好?
不是。只有高频读取确实需要的少量字段才值得考虑放进索引;宽索引会占空间,也会增加写入维护成本。
能直接在生产请求上跑 EXPLAIN ANALYZE 吗?
要谨慎。它会运行 SELECT 并收集测量结果。先在可控副本、低风险时间窗或等价测试数据上确认读取量,再决定是否需要线上抽样。
核对资料
- MySQL Reference Manual:LIMIT Query Optimization,核对 LIMIT 对有序索引、filesort、并列行顺序与
prefer_ordering_index的影响。 - MySQL Reference Manual:EXPLAIN Statement,核对 EXPLAIN 与 EXPLAIN ANALYZE 的语义。
- MySQL Reference Manual:How MySQL Uses Indexes,核对复合索引、覆盖索引和索引维护成本的边界。
RAG 知识库怎么防提示词注入:外部文档只能当资料,不能当指令
- 上一篇
- RAG 知识库怎么防提示词注入:外部文档只能当资料,不能当指令
- 下一篇
- 暂无
-
- 数据库 · MySQL | 1天前 | 并发 · MySQL · InnoDB · update · 库存扣减 · innodb MySQL 库存扣减 条件 UPDATE 防超卖 affected rows
- MySQL 库存怎么安全扣减?条件 UPDATE、防超卖和受影响行判断
- 470浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ljg-skills
- ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
- 4535次使用
-
- MELO音乐
- MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
- 4209次使用
-
- UniScribe
- UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
- 4168次使用
-
- 剧云
- 剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
- 4392次使用
-
- 万象有声
- 万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
- 4338次使用
-
- MySQL 明明加了索引,为什么查询还是很慢?先查这 6 个点
- 2026-06-27 374浏览
-
- 接口列表页越查越慢怎么办:N+1 查询从 120 次降到 3 次
- 2026-06-29 180浏览
-
- golang MySQL实现对数据库表存储获取操作示例
- 2022-12-22 499浏览
-
- golang 基于 mysql 简单实现分布式读写锁
- 2023-01-07 384浏览
-
- golang cache带索引超时缓存库实战示例
- 2022-12-31 234浏览

