当前位置: 首页 > news >正文

【MySQL篇】limit深度分页性能:从原理理解小偏移量limit 1,200 vs 百万级偏移量limit 1000000,200的差异

💫《博主主页》:奈斯DB-CSDN博客

🔥《擅长领域》:擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控;并对SQLserver、NoSQL(MongoDB)有了解

💖如果觉得文章对你有所帮助,欢迎点赞收藏加关注💖

    在日常数据库开发中,LIMIT分页是写SQL时最常用的语句。在实际使用分页语句分别执行 LIMIT 1,200 LIMIT 1000000,200 时,虽然两者都只返回200条数据,但后者却比前者慢了几个数量级。

    为了弄清这个问题背后的原因,博主也是查阅了一些网上的资料和MySQL8.0官方文档手册,研究了MySQL处理LIMIT分页的内部机制。理解这些底层原理,对于编写高效的SQL查询至关重要。只有掌握了LIMIT分页的工作原理,我们才能避免性能陷阱,让分页查询不再成为系统的瓶颈,才能更好地在SQL语句中使用LIMIT,优化查询性能。

                           

LIMIT 的基本原理:

语法:

SELECT * FROM table_name LIMIT [offset,] row_count;
     
参数说明:
row_count:指定要返回的最大行数
offset:指定要跳过的行数(从0开始计数)

                        

原理:

    MySQL 中的 LIMIT 子句用于限制查询返回的行数,其核心原理是在查询处理过程中提前终止结果集的生成。以下是 LIMIT 的主要工作机制:

  1. 查询执行过程中计数:MySQL 在执行查询时会维护一个计数器,每产生一行结果就递增

  2. 提前终止机制:当计数器达到 LIMIT 指定的行数时,立即停止查询执行

  3. 偏移处理:对于 LIMIT offset, count,MySQL 会先跳过 offset 行,然后开始计数

                       

全表扫描 + LIMIT:

            

使用索引 + LIMIT(最优情况):

       

ORDER BY + LIMIT 的两种处理方式:

    清楚了上面的原理,那么对比一下LIMIT 1,200和LIMIT 1000000,200

    LIMIT 分页实现机制决定了其性能特点:当执行 LIMIT m,n 时,数据库引擎需要先扫描并获取前 m+n 条完整记录,然后丢弃前 m 条,最终只返回剩下的 n 条结果。

    这种工作机制导致了一个关键的性能特征:分页查询的效率与偏移量 m 的大小直接相关。具体表现为:

  1.  LIMIT 1,200 只需读取 201 条数据,丢弃第一行数据然后返回

  2.  LIMIT 1000000,200 则需要先读取 1000200 条记录,再丢弃前 1000000 

    这种实现方式解释了为什么深度分页(大偏移量)的性能会显著下降。随着偏移量 m 的增加,数据库需要处理的数据量呈线性增长,即使最终返回的记录数 n 保持不变。这就是 LIMIT 1000000,200 LIMIT 1,200 慢得多的根本原因。

             

LIMIT 查询优化:

以下部分知识点来源于 MySQL 公开可查的官方文档手册(MySQL 8.0):https://dev.mysql.com/doc/refman/8.0/en/limit-optimization.html

    如果只需要从结果集中获取指定数量的行,请在查询中使用 LIMIT 子句,而不是获取整个结果集并丢弃多余的数据。

    MySQL 在某些情况下优化带有 LIMIT row_count 子句且没有 HAVING 子句的查询:

  • 如果只选择少量行并使用 LIMIT,MySQL 在某些情况下会使用索引,而通常情况下会选择进行全表扫描。

  • 如果将 LIMIT row_count ORDER BY 结合使用,MySQL 会在找到前 row_count 行的排序结果后立即停止排序,而不是对整个结果集进行排序。如果排序是通过索引完成的,这种操作非常快速。如果必须进行文件排序(filesort),MySQL 会先选择所有符合查询条件的行,并对它们进行排序,直到找到前 row_count 行为止。在找到初始的行之后,MySQL 不会再对剩余的结果集进行排序。

    这种行为的一个表现是,带 ORDER BY 和不带 LIMIT 的查询可能会返回不同顺序的行,正如下面所述。

  • 如果将 LIMIT row_count DISTINCT 结合使用,MySQL 会在找到 row_count 个唯一的行后立即停止。

  • 在某些情况下,GROUP BY 可以通过按索引顺序读取数据(或者对索引进行排序)来解决,然后计算汇总,直到索引值发生变化。在这种情况下,LIMIT row_count 不会计算不必要的 GROUP BY 值。

  • 一旦 MySQL 将所需数量的行发送到客户端,它就会中止查询,除非你使用了 SQL_CALC_FOUND_ROWS。在这种情况下,可以通过 SELECT FOUND_ROWS() 获取总行数。参见:https://dev.mysql.com/doc/refman/8.0/en/information-functions.html

  • LIMIT 0 会快速返回空结果集。这在检查查询有效性时非常有用,也可以用于在使用 MySQL API 的应用程序中获取结果列的类型。使用 mysql 客户端时,可以使用 --column-type-info 选项来显示结果列类型。

  • 如果服务器使用临时表来解决查询,它会使用 LIMIT row_count 子句来计算所需的空间。

  • 如果 ORDER BY 没有使用索引,但同时存在 LIMIT 子句,优化器可能能够避免使用合并文件(merge file),而是通过内存中的文件排序(in-memory filesort)操作对行进行排序。

  • 如果多行在 ORDER BY 列中具有相同的值,服务器可以自由地以任何顺序返回这些行,并且可能会根据整体执行计划以不同的顺序返回这些行。换句话说,这些行的排序顺序在非排序列上是非确定性的。

    一个影响执行计划的因素是 LIMIT,因此带有 LIMIT 和不带 LIMIT ORDER BY 查询可能会返回不同顺序的行。

                        

LIMIT 与 ORDER BY 配合使用的不确定性示例:

    当 ORDER BY 子句排序的字段存在重复值时,MySQL 对这些相同值的行记录可以以任意顺序返回。这种不确定性源于:

  1. 执行计划影响:MySQL 优化器可能选择不同的执行计划

  2. 存储引擎特性:不同存储引擎处理数据的方式不同

  3. 并行处理机制:多线程处理可能导致结果顺序变化

    因此,当 ORDER BY 语句中有 LIMIT 时,每次查询结果都有可能不同。

                       


            

    假设有一个员工表 employees,其中 department_id 字段有重复值:

-- 创建示例表
CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    department_id INT,
    salary DECIMAL(10,2)
);

-- 插入示例数据
INSERT INTO employees VALUES
(1, '张三', 101, 5000),
(2, '李四', 101, 6000),
(3, '王五', 102, 5500),
(4, '赵六', 102, 6500),
(5, '钱七', 101, 5200);

     不确定排序结果示例

-- 第一次查询可能返回
SELECT * FROM employees
ORDER BY department_id
LIMIT 3;

-- 结果可能为:
| id | name | department_id | salary |
|----|------|---------------|--------|
| 1  | 张三 | 101           | 5000   |
| 2  | 李四 | 101           | 6000   |
| 5  | 钱七 | 101           | 5200   |

-- 第二次相同查询可能返回
SELECT * FROM employees
ORDER BY department_id
LIMIT 3;

-- 结果可能变为:
| id | name | department_id | salary |
|----|------|---------------|--------|
| 5  | 钱七 | 101           | 5200   |
| 1  | 张三 | 101           | 5000   |
| 2  | 李四 | 101           | 6000   |

    要确保完全确定性的排序结果,应在 ORDER BY 子句中添加足够唯一的列,比如像 id 这样保证不会重复的字段:

-- 添加主键作为次要排序条件确保结果稳定
SELECT * FROM employees
ORDER BY department_id, id  -- id是主键,确保唯一性
LIMIT 3;

-- 这样每次查询都会返回相同顺序的结果
| id | name | department_id | salary |
|----|------|---------------|--------|
| 1  | 张三 | 101           | 5000   |
| 2  | 李四 | 101           | 6000   |
| 5  | 钱七 | 101           | 5200   |

         

LIMIT 与 ORDER BY 配合使用的不确定性实际应用建议:

  1. 在分页查询中,总是确保 ORDER BY 包含足够唯一的列

  2. 对于有重复值的排序字段,添加主键或其他唯一列作为次要排序条件

  3. 在需要精确排序的业务场景中,避免仅依赖可能有重复值的字段排序


 🌹 完结,撒花 🌹 

http://www.dtcms.com/a/108040.html

相关文章:

  • DirectX安装及使用教程(附安装包)
  • MongoDB安装完整教程
  • Transformer【学习记录】
  • react使用eventBus在不同模块间进行通信
  • Synology NAS 部署WPS-Office
  • zk基础—3.集群与核心参数二
  • 2025年优化算法:真菌生长优化算法(Fungal Growth Optimizer,FGO)
  • 【NetCore】ControllerBase:ASP.NET Core 中的基石类
  • 长短期记忆神经网络(LSTM)基础学习与实例:预测序列的未来
  • 外卖平台问题
  • 未来幻想世界
  • JAVA学习小计之IO流01-字节流篇
  • Axure 使用笔记
  • leetcode:3083. 字符串及其反转中是否存在同一子字符串(python3解法)
  • 算法设计与分析之“分治法”
  • Oracle常用高可用方案(10)——RAC
  • MFC BCGControlBar
  • 光谱相机的光谱数据采集原理
  • Python设计模式:代理模式
  • 看行业DeepSeekR1模型如何构建及减少推理大模型过度思考
  • IntelliJ IDEA全栈Git指南:从零构建到高效协作开发
  • 洛谷题单3-P1009 [NOIP 1998 普及组] 阶乘之和-python-流程图重构
  • vue中的 拖拽
  • @ComponentScan注解详解:Spring组件扫描的核心机制
  • 【力扣hot100题】(037)翻转二叉树
  • 每日一题---买卖股票的最好时机(一)、(二)
  • 【每日算法】Day 15-1:哈希表与布隆过滤器——海量数据处理与高效检索的核心技术(C++实现)
  • ollama本地部署大模型(命令行)
  • Eclipse IDE
  • 基本元素定位(findElement方法)