当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL 大表分页为什么越翻越慢:游标分页、复合索引和上一页游标设计

MySQL 大表分页为什么越翻越慢:游标分页、复合索引和上一页游标设计

来源:17golang原创 2026-08-25 03:55:07 0浏览 收藏

订单后台一开始只有几千行,翻页接口写成 LIMIT 100000, 20 也不觉得慢。数据涨到几千万后,第一页仍然很快,到了第 5000 页却要等一两秒,慢的不是返回 20 行,而是数据库为了找到这 20 行先处理了前面那一大段记录。

MySQL 大表分页越翻越慢,核心原因是传统 LIMIT offset,size 的写法需要先扫描并丢弃大量偏移位置的无效记录,当偏移量达到几万、几十万级别后,扫描和回表的开销会线性上涨,就算加了普通索引也很难解决根本问题,游标分页、复合索引的组合就是用来绕开这个性能瓶颈的常用方案。

大表分页越往后越慢的核心根源是深OFFSET需要跳过大量无效数据,基于上一页最后一条记录的全排序键做游标、搭配覆盖过滤条件+排序规则的复合索引,就能把分页查询的扫描成本稳定在常量级别。
要点速览
  • LIMIT offset,size 的深分页会让 MySQL 扫描并跳过大量候选行,页码越深成本越明显。
  • 游标分页要把过滤列、排序列和唯一键组合成稳定的复合索引,不能只按一个可能重复的时间字段翻页。
  • 上一页最后一条记录的排序键是下一页游标;游标必须带上同一排序规则需要的全部字段。
  • 数据会变化时,先明确“允许轻微变化”还是“固定快照”,不要把两种语义混在一个接口里。

深分页真正慢在哪里

假设订单列表只看 status = 'paid',按创建时间倒序、订单号倒序排列:

SELECT id, order_no, created_at, amount
FROM orders
WHERE status = 'paid'
ORDER BY created_at DESC, id DESC
LIMIT 100000, 20;

这里的 100000 不是“从磁盘的第 100001 行直接定位”。优化器需要沿着可用访问路径找到满足条件的记录,再把前 100000 个结果丢掉,最后才留下 20 个返回。索引能减少回表和排序,但不能让 OFFSET 本身消失。

MySQL 大表分页中 LIMIT OFFSET 扫描并丢弃大量记录,游标分页直接从上一页游标继续的对照图

先把分页模式说清楚:页码跳转还是连续浏览

游标分页不是所有列表的替代品。需要用户输入第 37 页、跳到最后一页,或者必须展示总页数时,传统页码仍然直观;新闻流、订单流水、审计记录和移动端“加载更多”则通常是连续向后取数据,更适合用上一页末尾的排序键继续查。

场景更合适的方式主要代价
后台页码跳转LIMIT/OFFSET深页扫描成本随 offset 增长
时间线连续加载游标分页不能天然跳到任意页
结果必须固定快照或版本号分页需要保存一致性边界

复合索引和游标要成对设计

示例接口按状态过滤,再按 created_atid 倒序排列,可以先准备这样的索引:

CREATE INDEX idx_status_created_id
ON orders (status, created_at, id);

第一列对应等值过滤,后两列对应稳定排序。id 很关键:同一秒可能有很多订单,只用 created_at 会让边界不唯一,下一页可能重复或漏掉记录。

第一页可以正常取:

SELECT id, order_no, created_at, amount
FROM orders
WHERE status = 'paid'
ORDER BY created_at DESC, id DESC
LIMIT 20;

假设这一页最后一行是 created_at = '2026-08-25 02:10:00'id = 880120,下一页就用这两个值组成游标:

SELECT id, order_no, created_at, amount
FROM orders
WHERE status = 'paid'
  AND (created_at 
MySQL 复合索引 idx_status_created_id 配合上一页游标和稳定排序读取下一页订单的工程示意图

为什么不能只用 created_at 做游标

如果条件只有 created_at ,上一页末尾时间相同的其他订单会被整体跳过。反过来,如果只写 created_at ,边界记录又可能重复出现。这个问题在毫秒精度不足、批量导入和同一事务集中写入时尤其常见。

因此游标要覆盖排序键的完整元组。排序是 created_at DESC, id DESC,比较条件也必须同时表达“时间更早”或“时间相同但 id 更小”。更换排序方向时,比较符号也要一起换,别只改 ORDER BY

把 EXPLAIN ANALYZE 作为上线前的核对点

不要只看接口响应时间。准备接近生产分布的数据,在 MySQL 上分别执行浅页和深页查询,观察实际读取行数、排序和回表情况:

EXPLAIN ANALYZE
SELECT id, order_no, created_at, amount
FROM orders
WHERE status = 'paid'
ORDER BY created_at DESC, id DESC
LIMIT 20;

再把游标条件替换成真实上一页边界。重点看访问行数是否随页码增长,是否使用了 idx_status_created_id,返回 20 行时是否额外读取了大批无关记录。这里别急着只盯着“用了索引”,索引命中不代表读取量已经合理。

常见反例:排序不稳定、游标失真和数据变化

排序字段重复却没有唯一补充键

同一时间戳下的记录没有确定先后,数据库可以在两次查询中给出不同顺序。把唯一递增键作为最后排序键,才能让边界可复现。

把前端显示页码当成游标

页码是展示概念,不是数据位置。游标应由服务端按排序键生成,前端只负责原样带回;不要让客户端修改时间戳或主键后再提交。

列表跨越了业务快照边界

游标分页强调“继续向前读”,期间新插入的记录可能出现在后续请求之前,已经读过的记录也可能被更新。若导出任务要求整批结果固定,应使用批次版本、业务截止时间或专门快照,而不是指望普通游标自动提供一致性快照。

上线前的判断清单

  • 排序是否包含唯一键,游标字段是否覆盖完整排序元组?
  • 过滤列和排序列是否对应同一个复合索引,索引顺序是否匹配查询条件?
  • 接口是否明确支持下一页,而不是暗中承诺任意页码跳转?
  • 新增、更新、删除发生时,产品是否接受列表轻微变化?
  • 是否用接近生产规模的数据执行过浅页、深页和空结果边界测试?

相关问题

游标分页还能返回 total 总数吗?

可以单独统计,但 COUNT(*) 可能成为另一条昂贵查询。若总数不是核心交互,通常返回是否还有下一页更划算。

为什么游标需要编码?

编码主要是避免把多个排序字段暴露成可编辑参数,并保持 URL 或请求体格式稳定;它不是安全边界,服务端仍要校验字段类型和查询范围。

MySQL 里可以直接用元组比较吗?

在排序方向一致、列语义明确时可以使用行构造器比较,但上线前要用 EXPLAIN ANALYZE 验证计划。混合 ASC、DESC 或需要兼容多版本时,显式 OR 条件更容易审查。

大表分页的关键不是把 SQL 改成某个固定模板,而是让“过滤、排序、边界”成为一个整体。连续浏览就把上一页最后的完整排序键交给下一页;需要固定结果则另行设计快照边界。两种需求分开,索引和接口都会更容易验收。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
GitHub Issues 如何用模板约束缺陷信息:从新建模板到提交后核对GitHub Issues 如何用模板约束缺陷信息:从新建模板到提交后核对
上一篇
GitHub Issues 如何用模板约束缺陷信息:从新建模板到提交后核对
GitHub CodeQL 2.26.3 更新了什么:Actions 查询识别与升级注意事项
下一篇
GitHub CodeQL 2.26.3 更新了什么:Actions 查询识别与升级注意事项
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ljg-skills -
    ljg-skills
    ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
    5235次使用
  • MELO音乐 - AI 音乐生成平台,支持多模态创作能力
    MELO音乐
    MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
    4740次使用
  • UniScribe - AI 免费在线音视频转文字平台
    UniScribe
    UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
    4691次使用
  • 剧云 - 免费 AI 智能中文剧本创作平台
    剧云
    剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
    4948次使用
  • 万象有声 - AI 一站式有声内容创作平台
    万象有声
    万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
    4907次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码