MySQL如何优化LIMIT深分页查询慢的问题_采用延迟关联与子查询技术

千墨君_4282

千墨君_4282

2026-05-30

224人浏览

原创

LIMIT 1000000,10会扫描1000010行再丢弃前100万行,因MySQL需逐行计数定位,若无覆盖索引还会频繁回表,导致IO和CPU开销剧增。

mysql如何优化limit深分页查询慢的问题_采用延迟关联与子查询技术

为什么 LIMIT 1000000, 10 会扫 100 万行才出结果

MySQL 执行 LIMIT offset, size 时,并不会跳过前 offset 行,而是从头开始逐行扫描、计数,直到累计够 offset + size 行,再丢弃前 offset 行。这意味着 LIMIT 1000000, 10 实际要读取并处理 1000010 行数据。

更糟的是:如果查询带 WHERE 和 ORDER BY,且排序字段没走覆盖索引,MySQL 还得反复回表——每扫一行就去主键索引捞一次完整记录。IO 和 CPU 开销指数级上升。

常见错误现象包括:

  • EXPLAIN 显示 rows 值极大(比如 >50 万),但 Extra 里还有 Using filesort 或 Using temporary
  • 同一语句在测试库秒出,在生产库查 5 秒以上,且 offset 每加 10 万,耗时明显递增
  • 慢日志里频繁出现 Query_time: 3.212 且 Rows_examined: 451350 这类组合

用子查询先捞 ID,再 JOIN 取全量字段

核心思路是把“查全量记录”拆成两步:第一步只走索引(甚至覆盖索引),快速定位目标 ID;第二步用这些 ID 精准回表。这样避免了大范围扫描和无效回表。

实操建议如下:

  • 子查询必须只 SELECT id,且 ORDER BY 字段需有索引(最好是主键或联合索引最左前缀)
  • 外层 JOIN 比 IN 更可靠——MySQL 8.0 以前不支持 IN 子句里用 LIMIT
  • 复合条件要下推到子查询里,比如 WHERE update_time > '2025-01-01' 必须写在子查询中,否则外层 JOIN 会放大结果集
  • 示例语句:
    SELECT a.* FROM account a
    INNER JOIN (
      SELECT id FROM account 
      WHERE update_time > '2025-01-01' 
      ORDER BY id 
      LIMIT 1000000, 10
    ) b ON a.id = b.id;

用 WHERE id > ? 替代大 offset,但只适用于顺序翻页

当业务允许“一页页往下刷”(如 APP 下拉加载),直接用上一页最后的 id 作为下一页起点,性能飞跃。此时 MySQL 只需在主键 B+ 树叶子节点链表上往后走 size 条,完全规避偏移计算。

MySQL
MySQL

编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。

下载

关键约束和陷阱:

  • 必须确保 ORDER BY 字段是**严格单调唯一**的(推荐用主键 id,慎用 update_time——可能有重复值)
  • 不能跳页:用户从第 1 页直接点第 500 页,此法失效;需配合游标(cursor)参数传递,后端不存状态
  • 示例:
    SELECT id, name, balance FROM account 
    WHERE id > 6800000 
    ORDER BY id 
    LIMIT 10;
  • 如果排序依据是时间字段,且存在重复,可追加主键去重:ORDER BY update_time DESC, id DESC,对应查询条件也要补上:WHERE (update_time, id)

为什么复合索引对子查询优化至关重要

子查询 SELECT id FROM t WHERE x = ? ORDER BY y LIMIT m,n 能否走索引、是否产生临时表,完全取决于索引设计。若 WHERE 和 ORDER BY 涉及不同字段,又没建好复合索引,子查询本身就会变慢,优化就白搭。

正确做法:

  • 把 WHERE 条件字段放在复合索引前面,ORDER BY 字段放后面,例如:KEY idx_update_time_id (update_time, id)
  • 如果 SELECT 列全在索引里(如只查 id),就是覆盖索引,连主键回表都省了
  • 避免在子查询里用函数或表达式,比如 WHERE DATE(update_time) = '2025-05-10' 会让索引失效
  • 用 EXPLAIN 验证子查询是否走了索引:type 应为 range 或 ref,key 显示实际使用的索引名,Extra 不含 Using filesort 或 Using temporary

真正难的不是写出优化 SQL,而是让所有分页路径都收敛到同一个索引策略上——尤其是当 WHERE 条件动态拼接、前端传参不固定时,很容易漏建某个组合的索引,导致某类请求突然变慢。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

mysql mysql延迟关联

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4583

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

951

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1209

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

6561

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3323

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

6520

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

8961

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1210

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1072

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 192人学习