MySQL深度分页问题的三种解决方法

 更新时间:2025年02月28日 09:25:09   作者:Dolphin_Home  
在 MySQL 中解决深度分页问题的核心思路是减少扫描的数据量,尤其是避免通过 LIMIT offset, size 导致的大范围数据扫描,以下是三种优化方法及其原理、适用场景和注意事项,需要的朋友可以参考下

前言

在 MySQL 中解决深度分页问题的核心思路是减少扫描的数据量,尤其是避免通过 LIMIT offset, size 导致的大范围数据扫描。以下是三种优化方法及其原理、适用场景和注意事项:

1. 子查询 + 覆盖索引(延迟关联)

原理

  • 先通过覆盖索引(如二级索引 (name, id))快速定位目标页的起始 id,再通过主键索引回表查询数据。
  • 子查询只需扫描二级索引,体积小且有序,能高效跳过 offset 行,获取起始 id
  • 主查询通过 id >= [子查询结果] 直接定位数据,避免全表扫描。

示例 SQL

SELECT * FROM mianshiya 
WHERE name = 'yupi' AND id >= (
    SELECT id FROM mianshiya 
    WHERE name = 'yupi' 
    ORDER BY id LIMIT 99999990, 1
)
ORDER BY id LIMIT 10;

或使用 JOIN 优化:

SELECT * FROM mianshiya 
INNER JOIN (
    SELECT id FROM mianshiya 
    WHERE name = 'yupi' 
    ORDER BY id LIMIT 99999990, 10
) AS tmp ON mianshiya.id = tmp.id;

关键点

  • 必须创建联合索引 (name, id),确保子查询直接利用索引有序性,避免临时排序(filesort)。
  • 主查询的 name 条件可省略(若子查询结果 id 对应的 name 必为 'yupi'),但需权衡数据变更风险。

2. 记录最大 ID(游标分页)

原理

  • 每次分页返回当前页的最大 id,下页查询时通过 WHERE id > max_id LIMIT size 跳过已读数据。
  • 仅扫描目标数据(size 行),时间复杂度稳定为 O(size),性能极佳。

适用场景

  • 连续分页(如“下一页”),不支持随机跳页。
  • 数据按主键或有序字段分页(如 ORDER BY id)。

示例 SQL

-- 第一页
SELECT * FROM mianshiya WHERE name = 'yupi' ORDER BY id LIMIT 10;
-- 后续页(假设上一页最大 id 为 100)
SELECT * FROM mianshiya WHERE name = 'yupi' AND id > 100 ORDER BY id LIMIT 10;

注意事项

  • 若数据删除或新增可能导致少量重复或遗漏,需业务容忍。
  • 需前端配合传递 max_id,不可直接跳页。

3. Elasticsearch 优化

原理

  • 将数据同步到 Elasticsearch,利用其分布式特性加速搜索。
  • 通过 search_after 参数实现游标分页,类似记录 max_id 方法,避免深分页性能问题。

ES 分页示例

{
  "query": { "match": { "name": "yupi" } },
  "sort": [{ "id": "asc" }],
  "size": 10,
  "search_after": [100]  -- 上一页最后一条记录的排序值
}

ES 注意事项

  • 默认限制 from + size <= 10000,深度分页需改用 search_after 或 scroll
  • search_after 需基于唯一排序字段,且仅支持连续分页。

其他优化思路

  • 业务限制:禁止深度跳页(如最多展示前 100 页)。
  • 预计算分页位置:按时间或分段存储分页起始 ID(如每 1000 页记录一个起始 ID)。
  • 分区表:按时间或范围分区,减少单次扫描数据量。

总结

方法优点缺点适用场景
子查询 + 覆盖索引支持跳页,通用性强依赖联合索引设计需频繁分页且允许跳页
记录最大 ID性能最优,复杂度低仅支持连续分页连续分页(如瀑布流)
Elasticsearch适合复杂搜索,分布式性能好维护成本高,ES 需额外学习高并发搜索+分页场景

核心要点

  • 索引设计是优化基础,确保覆盖索引和排序字段匹配。
  • 权衡业务需求,优先选择游标分页(记录 max_id)或延迟关联(子查询)。
  • 搜索引擎适用于复杂查询,但引入额外组件需评估成本。

扩展:MySQL中 join、inner join、left join、right join区别

在 MySQL 中,JOIN 操作用于将多个表中的数据关联起来。以下是 INNER JOINLEFT JOINRIGHT JOIN 的核心区别和用法总结,帮助你快速分清它们:

1. INNER JOIN(内连接)

  • 作用:只返回两个表中完全匹配的行。

  • 结果:仅包含两个表中都存在关联键值的行。

  • 语法

SELECT * 
FROM 表A
INNER JOIN 表B ON 表A.键 = 表B.键;
  • 示例

    • 表A(员工表)和表B(部门表)通过 department_id 关联。
    • 结果:只显示有部门的员工信息,无部门无员工的数据会被过滤掉。

2. LEFT JOIN(左外连接)

  • 作用:返回左表(LEFT JOIN 左侧的表)的所有行,即使右表没有匹配。

  • 结果:左表所有数据 + 右表匹配的数据(无匹配时右表字段为 NULL)。

  • 语法

SELECT * 
FROM 表A
LEFT JOIN 表B ON 表A.键 = 表B.键;
  • 示例

    • 表A(员工表)LEFT JOIN 表B(部门表)。
    • 结果:显示所有员工信息,即使员工没有部门(部门字段为 NULL)。

3. RIGHT JOIN(右外连接)

  • 作用:返回右表(RIGHT JOIN 右侧的表)的所有行,即使左表没有匹配。

  • 结果:右表所有数据 + 左表匹配的数据(无匹配时左表字段为 NULL)。

  • 语法

SELECT * 
FROM 表A
RIGHT JOIN 表B ON 表A.键 = 表B.键;
  • 示例

    • 表A(员工表)RIGHT JOIN 表B(部门表)。
    • 结果:显示所有部门信息,即使部门没有员工(员工字段为 NULL)。

4. JOIN(默认是 INNER JOIN)

说明:在 MySQL 中,直接写 JOIN 等价于 INNER JOIN

SELECT * 
FROM 表A
JOIN 表B ON 表A.键 = 表B.键; -- 等同于 INNER JOIN

对比总结

类型行为适用场景
INNER JOIN仅返回两个表匹配的行需要精确匹配的数据(如订单和商品)
LEFT JOIN返回左表全部数据 + 右表匹配的数据(右表无匹配则为 NULL保留左表全部数据(如所有员工信息)
RIGHT JOIN返回右表全部数据 + 左表匹配的数据(左表无匹配则为 NULL保留右表全部数据(如所有部门信息)

关键注意事项

  1. 方向性LEFT JOIN 和 RIGHT JOIN 的方向取决于表的书写顺序。
    • LEFT JOIN 以左表为主,RIGHT JOIN 以右表为主。
  2. 过滤条件
    • 在 LEFT JOIN 中,若在 WHERE 子句中对右表字段过滤(如 WHERE 表B.键 IS NULL),会筛选出仅存在于左表但右表无匹配的行。
  3. 性能
    • INNER JOIN 通常效率更高,因为它涉及的数据量更小。
    • LEFT/RIGHT JOIN 可能因处理 NULL 值而略慢,尤其是在大表中。

示例演示

数据准备
-- 员工表(employees)
+-------------+-------+---------------+
| employee_id | name  | department_id |
+-------------+-------+---------------+
| 1           | 张三  | 101           |
| 2           | 李四  | 102           |
| 3           | 王五  | NULL          |
+-------------+-------+---------------+

-- 部门表(departments)
+---------------+-----------------+
| department_id | department_name |
+---------------+-----------------+
| 101           | 技术部          |
| 102           | 市场部          |
| 103           | 财务部          |
+---------------+-----------------+

查询结果对比

INNER JOIN(匹配数据):

SELECT * 
FROM employees
INNER JOIN departments 
  ON employees.department_id = departments.department_id;

结果

| 1 | 张三 | 101 | 101 | 技术部 |
| 2 | 李四 | 102 | 102 | 市场部 |

LEFT JOIN(保留所有员工):

SELECT * 
FROM employees
LEFT JOIN departments 
  ON employees.department_id = departments.department_id;

结果

| 1 | 张三 | 101 | 101 | 技术部 |
| 2 | 李四 | 102 | 102 | 市场部 |
| 3 | 王五 | NULL| NULL| NULL   | -- 员工无部门,右表字段为 NULL

RIGHT JOIN(保留所有部门):

SELECT * 
FROM employees
RIGHT JOIN departments 
  ON employees.department_id = departments.department_id;

结果

| 1 | 张三 | 101 | 101 | 技术部 |
| 2 | 李四 | 102 | 102 | 市场部 |
| NULL| NULL| NULL| 103 | 财务部 | -- 部门无员工,左表字段为 NULL

总结

  • INNER JOIN:精确匹配,适合需要严格关联的场景。
  • LEFT JOIN:保留左表全部数据,适合主从表查询(如“所有员工及其部门”)。
  • RIGHT JOIN:保留右表全部数据,使用较少(通常用 LEFT JOIN 调换表顺序替代)。

以上就是MySQL深度分页问题的三种解决方法的详细内容,更多关于MySQL深度分页问题的资料请关注脚本之家其它相关文章!

相关文章

  • Mysql LONGBLOB 类型存储二进制数据 (修改+调试+整理)

    Mysql LONGBLOB 类型存储二进制数据 (修改+调试+整理)

    代码来自网络,我学习整理了一下,测试通过,下面的参数需要设置为你自己的
    2009-07-07
  • Ubuntu20下MySQL 8.0.28 安装卸载方法图文教程

    Ubuntu20下MySQL 8.0.28 安装卸载方法图文教程

    这篇文章主要为大家详细介绍了Ubuntu20下MySQL 8.0.28 安装卸载方法图文教程,文中安装步骤介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • 安装Mysql时出现错误及解决办法

    安装Mysql时出现错误及解决办法

    因为一时手痒痒更新了一下驱动,结果导致无线网卡出了问题,本文给大家分享安装mysql时出现错误及解决办法,对安装mysql时出现错误相关知识感兴趣的朋友一起学习吧
    2015-12-12
  • mysql事务隔离级别详解

    mysql事务隔离级别详解

    MySQL事务隔离级别是指在多个事务同时执行时,数据库系统如何处理这些事务之间的相互影响。MySQL提供了四种隔离级别:读未提交、读已提交、可重复读和串行化。每种隔离级别都有其优缺点,需要根据具体情况选择合适的级别。
    2023-06-06
  • MySQL Semisynchronous Replication介绍

    MySQL Semisynchronous Replication介绍

    这篇文章主要介绍了MySQL Semisynchronous Replication介绍,本文讲解了Semisynchronous Replication 定义、,需要的朋友可以参考下
    2015-05-05
  • MySQL逻辑删除与唯一索引冲突解决方案

    MySQL逻辑删除与唯一索引冲突解决方案

    本文探讨MySQL逻辑删除与唯一索引冲突问题,提出四种解决方案:复合索引+时间戳、修改唯一字段、历史表、业务层校验,推荐方案1和方案3,适用于不同场景,感兴趣的朋友一起看看吧
    2025-07-07
  • MySQL的事务的基本要素和事务隔离级别详解

    MySQL的事务的基本要素和事务隔离级别详解

    这篇文章主要介绍了MySQL的事务的基本要素和事务隔离级别,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • mysql中json_extract的使用方法实例详解

    mysql中json_extract的使用方法实例详解

    MYSQl自带的解析函数JSON_EXTRACT,用JSON_EXTRACT函数解析出来的函数会包含双引号,下面这篇文章主要给大家介绍了关于mysql中json_extract的使用方法,需要的朋友可以参考下
    2023-04-04
  • MySQL日志专项之redo log和undo log介绍

    MySQL日志专项之redo log和undo log介绍

    MySQL日志记录了MySQL数据库日常操作和错误信息,MySQL有不同类型的日志文件(各自存储了不同类型的日志),从日志当中可以查询到MySQL数据库的运行情况、用户操作、错误信息等
    2022-08-08
  • MySQL的慢日志线上问题及优化方案

    MySQL的慢日志线上问题及优化方案

    给大家详细分析了MySQL慢日志线上问题分析及功能优化方案,需要的朋友跟着学习下吧。
    2017-12-12

最新评论