一、前言:深分页是数据库最常见的性能陷阱
大家好,我是程序员卷卷狗。
在实际项目中,分页查询是最常见的 sql 场景之一。但随着业务数据量不断增长,我们经常会遇到类似的请求:
select * from order limit 100000, 20;
看似普通的分页,却是 mysql 性能下降最典型的原因之一。深分页会导致大量无效扫描,使数据库压力急剧上升,严重时甚至拖垮整个系统。
理解 mysql 深分页的本质,以及掌握高性能替代方案,是后端开发必须具备的能力。
二、深分页为什么慢:mysql 的扫描机制决定了性能上限
mysql 在执行 limit offset, size 时,后台需要先扫描 offset+size 行,再丢弃前 offset 行,最后只返回 size 行。
例如:
limit 100000, 20;
实际上 mysql 做了:
- 扫描 100020 行
- 丢弃前 100000 行
- 返回最后 20 行
这意味着:offset 越大,mysql 的扫描成本越高。
深分页本质上是:大量无意义的扫描与丢弃操作导致性能变差。
三、传统的 limit 深分页问题实例
假设 order 表有 500 万行。
执行:
select * from order order by id limit 3000000, 20;
执行过程如下:
- 扫描 3000020 行
- 丢掉 3000000 行
- 返回 20 行
在 innodb 中,行是按主键组织的,因此需要大量磁盘随机读,性能极其低下。
四、深分页优化方案一:利用索引覆盖 + 子查询替代 offset
最广泛使用的方法是:先查主键,再回查数据
示例:
select *
from order o
join (
select id
from order
order by id
limit 100000, 20
) tmp on o.id = tmp.id;
优势:
- 子查询只扫描主键索引,成本远低于扫描整行
- 回表只发生 20 次
- 适用于大部分分页场景
这是后端分页中最通用的优化方式。
五、深分页优化方案二:基于主键条件的“游标式分页”
核心思想:只查询比上一页最后一条记录大的数据
示例:
select *
from order
where id > #{lastid}
order by id
limit 20;
效果:
- 不存在 offset
- 不需要无效扫描
- 执行速度稳定
- 延迟极低
适用于:
- 按主键(或有序字段)分页
- 下拉加载、滚动加载
- 长列表查询
这是现代后端系统最推荐的分页方式。
六、深分页优化方案三:使用延迟关联减少扫描
对于关联查询,可以先通过索引获取主键,再做回表关联。
示例:
select o.*
from order o
join (
select id
from order
where status = 1
order by id
limit 100000, 20
) t on o.id = t.id;
优点:
- 外层只回表 20 行
- 内层查询只扫描索引
- 大幅降低磁盘 i/o
适用于:
- 过滤条件多
- 需要使用复合索引
- 单表数据量大
七、深分页优化方案四:反向分页
如果用户想看最后几页:
select * from order order by id desc limit 20 offset 100000;
可以转换为:
select * from order order by id asc limit total - 100000 - 20, 20;
减少扫描量,提升性能。
适用于:
- 翻页至尾部页面的场景
- 数据倾斜导致深分页的场景
八、深分页优化方案五:通过业务改造避免深分页
包括:
- 限制最大页数
- 滚动分页替代页码分页
- 使用搜索条件缩减数据量
- 使用缓存 + 分段加载
- 使用 es、clickhouse 等搜索引擎替代 mysql 深分页查询
深分页本质上是业务问题,避免深分页比优化深分页更高效。
九、面试高频问题与标准回答
问:mysql 为什么深分页会慢?
答:limit offset,size 会导致 mysql 实际扫描 offset+size 行,再丢弃前 offset 行,随着 offset 增大,会出现大量无效扫描,磁盘 i/o 和 cpu 消耗急剧增加。
问:如何优化 limit 100000,20?
答:可以通过索引覆盖查询、延迟关联、主键游标分页等方式,将 offset 分离为基于主键或索引的范围过滤,从而避免大量无效扫描。
问:游标分页的原理是什么?
答:通过记录上一页的最大主键,将下一页限制为 where id > lastid 的形式,使分页不再依赖 offset,提高查询效率。
问:什么时候必须放弃 mysql 分页?
答:当查询数据量非常巨大且业务允许时,可以将搜索功能迁移到 elasticsearch 或 clickhouse,提高深分页性能。
十、总结
深分页的性能问题来自 mysql 扫描机制本身,而不是 sql 写得好坏。
真正的解决方案在于:
- 用主键分页替代 offset
- 用索引覆盖替代全表扫描
- 用延迟关联减少回表次数
- 用业务手段避免深分页
- 在极端场景下采用专业搜索引擎
一句话总结:
深分页的关键不是查询更多数据,而是避免不必要的扫描。
到此这篇关于mysql中深分页limit 100000的优化方案的文章就介绍到这了,更多相关mysql深分页内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论