MySQL大数据量分页慢本质是OFFSET越大扫描行数越多;应改用游标分页,即记录上页末条排序值,下页查大于该值的记录,并避免SQL_CALC_FOUND_ROWS及依赖ID连续性。

MySQL大数据量分页查询慢,本质是OFFSET 越大,扫描行数越多。比如 SELECT * FROM t ORDER BY id LIMIT 1000000, 20,MySQL 仍需先跳过前100万行,再取20条——这过程不走索引优化,I/O 和 CPU 开销陡增。真正有效的优化不是“加索引”就完事,而是绕开 OFFSET 的低效机制。
用游标分页(Cursor-based Pagination)替代 OFFSET
适合按时间、ID 等单调字段排序的场景(如最新订单、文章列表)。核心是“记住上一页最后一条的排序值”,下一页直接查“大于该值”的记录。
- ✅ 原始写法(慢):
SELECT * FROM orders ORDER BY created_at DESC LIMIT 10000, 20 - ✅ 优化写法(快):
SELECT * FROM orders WHERE created_at ,前提是上一页最后一条的created_at是这个时间 - ⚠️ 注意:必须确保排序字段+查询条件能唯一确定位置(可联合主键或加
id防止时间重复)
延迟关联(Deferred Join)减少回表开销
当需要分页返回大量字段,但排序/过滤只依赖少量字段时,先用覆盖索引快速定位 ID,再关联原表取数据。
免费的开源程序长期以来,为中国的网上交易提供免费开源的网上商店系统一直是我们的初衷和努力奋斗的目标,希望大家一起把MvMmall网上商店系统的免费开源进行到底。2高效的执行效率由资深的开发团队设计,从系统架构,数据库优化,配以通过W3C验证的面页模板,全面提升页面显示速度和提高程序负载能力。3灵活的模板系统MvMmall网店系统程序代码与网页界面分离,灵活的模板方案,完全自定义模板,官方提供免费模
- ✅ 示例:对
user_id排序查用户详情SELECT u.* FROM users u INNER JOIN (SELECT id FROM users ORDER BY user_id LIMIT 100000, 20) t ON u.id = t.id - ✅ 关键:子查询只走索引(如
INDEX(user_id)),不读全行;外层再按 ID 主键回表,效率远高于全字段扫描 - ⚠️ 要求:子查询中的排序字段必须有高效索引,且尽量避免
SELECT *在子查询中
物理分表 + 分页路由(适用于亿级单表)
单表超 5000 万行后,即使优化 SQL,维护成本和查询抖动仍高。可按时间(如月表 orders_202405)或哈希(如 user_id % 16)拆分,分页请求由应用层计算落到哪张子表。
- ✅ 每个子表数据量可控(如 500 万),
LIMIT 10000,20变成轻量操作 - ✅ 结合游标分页,在子表内继续优化,效果叠加
- ⚠️ 需配套方案:全局唯一 ID、跨表聚合查询(如“最新100条”需 union all 多表)、分页状态维护(如前端传
last_id和table_suffix)
补充技巧:避免踩坑
有些“看似优化”的做法实际无效甚至更差:
- ❌
WHERE id > ? ORDER BY id LIMIT 20:若 ID 不连续(删过数据),会跳过记录,造成分页漏数 - ❌ 仅靠
SQL_CALC_FOUND_ROWS:获取总页数仍要全表扫描,大数据下比直接COUNT(*)还慢 - ✅ 替代总页数:显示“下一页”按钮 + “已加载 20 条”,或用估算(如
EXPLAIN的rows× 抽样比例) - ✅ 强制使用索引:
FORCE INDEX(idx_created_at)防止优化器选错执行计划









