深度分页介绍及优化建议

深度分页介绍

查询偏移量过大的场景我们称为深度分页,这会导致查询性能较低,例如:

# MySQL 在无法利用索引的情况下跳过1000000条记录后,再获取10条记录
SELECT * FROM t_order ORDER BY id LIMIT 1000000, 10

深度分页问题的原因

当查询偏移量过大时,MySQL 的查询优化器可能会选择全表扫描而不是利用索引来优化查询。这是因为扫描索引和跳过大量记录可能比直接全表扫描更耗费资源。

不同机器上这个查询偏移量过大的临界点可能不同,取决于多个因素,包括硬件配置(如 CPU 性能、磁盘速度)、表的大小、索引的类型和统计信息等。

MySQL 的查询优化器采用基于成本的策略来选择最优的查询执行计划。它会根据 CPU 和 I/O 的成本来决定是否使用索引扫描或全表扫描。如果优化器认为全表扫描的成本更低,它就会放弃使用索引。不过,即使偏移量很大,如果查询中使用了覆盖索引(covering index),MySQL 仍然可能会使用索引,避免回表操作。

深度分析优化建议

这里以 MySQL 数据库为例介绍一下如何优化深度分页。

范围查询

当可以保证 ID 的连续性时,根据 ID 范围进行分页是比较好的解决方案:

# 查询指定 ID 范围的数据
SELECT * FROM t_order WHERE id > 100000 AND id <= 100010 ORDER BY id
# 也可以通过记录上次查询结果的最后一条记录的ID进行下一页的查询:
SELECT * FROM t_order WHERE id > 100000 LIMIT 10

这种基于 ID 范围的深度分页优化方式存在很大限制:

  1. ID 连续性要求高: 实际项目中,数据库自增 ID 往往因为各种原因(例如删除数据、事务回滚等)导致 ID 不连续,难以保证连续性。
  2. 排序问题: 如果查询需要按照其他字段(例如创建时间、更新时间等)排序,而不是按照 ID 排序,那么这种方法就不再适用。
  3. 并发场景: 在高并发场景下,单纯依赖记录上次查询的最后一条记录的 ID 进行分页,容易出现数据重复或遗漏的问题。

子查询

我们先查询出 limit 第一个参数对应的主键值,再根据这个主键值再去过滤并 limit,这样效率会更快一些。

阿里巴巴《Java 开发手册》中也有对应的描述:

利用延迟关联或者子查询优化超多分页场景。

# 通过子查询来获取 id 的起始值,把 limit 1000000 的条件转移到子查询
SELECT * FROM t_order WHERE id >= (SELECT id FROM t_order where id > 1000000 limit 1) LIMIT 10;

工作原理:

  1. 子查询 (SELECT id FROM t_order where id > 1000000 limit 1) 会利用主键索引快速定位到第 1000001 条记录,并返回其 ID 值。
  2. 主查询 SELECT * FROM t_order WHERE id >= ... LIMIT 10 将子查询返回的起始 ID 作为过滤条件,使用 id >= 获取从该 ID 开始的后续 10 条记录。

不过,子查询的结果会产生一张新表,会影响性能,应该尽量避免大量使用子查询。并且,这种方法只适用于 ID 是正序的。在复杂分页场景,往往需要通过过滤条件,筛选到符合条件的 ID,此时的 ID 是离散且不连续的。

当然,我们也可以利用子查询先去获取目标分页的 ID 集合,然后再根据 ID 集合获取内容,但这种写法非常繁琐,不如使用 INNER JOIN 延迟关联。

延迟关联

延迟关联与子查询的优化思路类似,都是通过将 LIMIT 操作转移到主键索引树上,减少回表次数。相比直接使用子查询,延迟关联通过 INNER JOIN 将子查询结果集成到主查询中,避免了子查询可能产生的临时表。在执行 INNER JOIN 时,MySQL 优化器能够利用索引进行高效的连接操作(如索引扫描或其他优化策略),因此在深度分页场景下,性能通常优于直接使用子查询。

-- 使用 INNER JOIN 进行延迟关联
SELECT t1.*
FROM t_order t1
INNER JOIN (SELECT id FROM t_order where id > 1000000 LIMIT 10) t2 ON t1.id = t2.id;

工作原理:

  1. 子查询 (SELECT id FROM t_order where id > 1000000 LIMIT 10) 利用主键索引快速定位目标分页的 10 条记录的 ID。
  2. 通过 INNER JOIN 将子查询结果与主表 t_order 关联,获取完整的记录数据。

除了使用 INNER JOIN 之外,还可以使用逗号连接子查询。

-- 使用逗号进行延迟关联
SELECT t1.* FROM t_order t1,
(SELECT id FROM t_order where id > 1000000 LIMIT 10) t2
WHERE t1.id = t2.id;

注意: 虽然逗号连接子查询也能实现类似的效果,但为了代码可读性和可维护性,建议使用更规范的 INNER JOIN 语法。

覆盖索引

索引中已经包含了所有需要获取的字段的查询方式称为覆盖索引。

覆盖索引的好处:

  • 避免 InnoDB 表进行索引的二次查询,也就是回表操作: InnoDB 是以聚集索引的顺序来存储的,对于 InnoDB 来说,二级索引在叶子节点中所保存的是行的主键信息,如果是用二级索引查询数据的话,在查找到相应的键值后,还要通过主键进行二次查询才能获取我们真实所需要的数据。而在覆盖索引中,二级索引的键值中可以获取所有的数据,避免了对主键的二次查询(回表),减少了 IO 操作,提升了查询效率。
  • 可以把随机 IO 变成顺序 IO 加快查询效率: 由于覆盖索引是按键值的顺序存储的,对于 IO 密集型的范围查找来说,对比随机从磁盘读取每一行的数据 IO 要少的多,因此利用覆盖索引在访问时也可以把磁盘的随机读取的 IO 转变成索引查找的顺序 IO。
# 如果只需要查询 id, code, type 这三列,可建立 code 和 type 的覆盖索引
SELECT id, code, type FROM t_order
ORDER BY code
LIMIT 1000000, 10;

⚠️注意:

  • 当查询的结果集占表的总行数的很大一部分时,MySQL 查询优化器可能选择放弃使用索引,自动转换为全表扫描。
  • 虽然可以使用 FORCE INDEX 强制查询优化器走索引,但这种方式可能会导致查询优化器无法选择更优的执行计划,效果并不总是理想。

总结

本文总结了几种常见的深度分页优化方案:

  1. 范围查询: 基于 ID 连续性进行分页,通过记录上一页最后一条记录的 ID 来获取下一页数据。适合 ID 连续且按 ID 查询的场景,但在 ID 不连续或需要按其他字段排序时存在局限。
  2. 子查询: 先通过子查询获取分页的起始主键值,再根据主键进行筛选分页。利用主键索引提高效率,但子查询会生成临时表,复杂场景下性能不佳。
  3. 延迟关联 (INNER JOIN): 使用 INNER JOIN 将分页操作转移到主键索引上,减少回表次数。相比子查询,延迟关联的性能更优,适合大数据量的分页查询。
  4. 覆盖索引: 通过索引直接获取所需字段,避免回表操作,减少 IO 开销,适合查询特定字段的场景。但当结果集较大时,MySQL 可能会选择全表扫描。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/72653.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

live555推流服务器异常

1.后端异常信息&#xff1a; MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size (100176). 48899 bytes of trailing data was dropped! Correct this by increasing "OutPacketBuffer::maxSize" to at least m…

每日OJ_牛客_宵暗的妖怪_DP_C++_Java

目录 牛客_宵暗的妖怪_DP 题目解析 C代码 Java代码 牛客_宵暗的妖怪_DP 宵暗的妖怪 描述&#xff1a; 露米娅作为宵暗的妖怪&#xff0c;非常喜欢吞噬黑暗。这天&#xff0c;她来到了一条路上&#xff0c;准备吞噬这条路上的黑暗。这条道路一共被分为n 部分&…

20250306-笔记-精读class CVRPEnv:step(self, selected)

文章目录 前言一、if self.time_step<4:控制时间步的递增判断是否在配送中心特定时间步的操作更新更新当前节点和已选择节点列表更新需求和负载更新访问标记更新负无穷掩码更新步骤状态&#xff0c;将更新后的状态同步到 self.step_state 二、使用步骤总结 前言 class CVRP…

Flowable 基本入门

flowable.7z官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘 1、Flowable介绍 Flowable是BPMN的一个基于java的软件实现&#xff0c;不过Flowable不仅仅包括BPMN&#xff0c;还有DMN决策表和CMMN Case管理引擎&#xff0c;并且有自己的用户管理、微服务API等一系列功能&a…

完全背包-一维数组

52. 携带研究材料&#xff08;第七期模拟笔试&#xff09; 题目描述 小明是一位科学家&#xff0c;他需要参加一场重要的国际科学大会&#xff0c;以展示自己的最新研究成果。他需要带一些研究材料&#xff0c;但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和…

景联文科技:以专业标注赋能AI未来,驱动智能时代的精准跃迁

在人工智能技术重塑全球产业格局的今天&#xff0c;高质量训练数据已成为驱动算法进化的核心燃料。作为数据智能服务领域的领军者&#xff0c;景联文科技深耕数据标注行业多年&#xff0c;以全栈式数据解决方案为核心&#xff0c;构建起覆盖数据采集、清洗、标注、质检及算法调…

洛谷B2074 计算星期几

B2074 计算星期几 - 洛谷 代码区&#xff1a; #include<algorithm> #include<iostream> #include<unordered_map> #include<string> using namespace std; int main() {unordered_map<int, string> m { { 1,"Monday" },{2,"Tue…

协同过滤推荐算法+微信小程序的农产品团购推荐平台(程序+论文+讲解+安装+调试+售后)

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统介绍 在当今时代&#xff0c;科学技术正以令人瞩目的速度迅猛进步&#xff0c;经济社会也随之…

十大经典排序算法简介

一 概述 本文对十大经典排序算法做简要的总结(按常用分类方式排列),包含核心思想、时间/空间复杂度及特点。 二、比较类排序 1. 冒泡排序 (BUBBLE SORT) 思想:重复交换相邻逆序元素,像气泡上浮 复杂度: 时间:O(n^2)(最好情况O(n)) 空间:O(1) 特点:简单但效率低,稳…

[自然语言处理]pytorch概述--什么是张量(Tensor)和基本操作

pytorch概述 PyTorch 是⼀个开源的深度学习框架&#xff0c;由 Facebook 的⼈⼯智能研究团队开发和维护&#xff0c;于2017年在GitHub上开源&#xff0c;在学术界和⼯业界都得到了⼴泛应⽤ pytorch能做什么 GPU加速自动求导常用网络层 pytorch基础 量的概念 标量&#xf…

Spring统一格式返回

目录 一&#xff1a;统一结果返回 1&#xff1a;统一结果返回写法 2&#xff1a;String类型报错问题 解决方法 二&#xff1a;统一异常返回 统一异常返回写法 三&#xff1a;总结 同志们&#xff0c;今天咱来讲一讲统一格式返回啊&#xff0c;也是好久没有讲过统一格式返…

【无标题】四色拓扑模型与宇宙历史重构的猜想框架

### 四色拓扑模型与宇宙历史重构的猜想框架 --- #### **一、理论基础&#xff1a;四色拓扑与时空全息原理的融合** 1. **宇宙背景信息的拓扑编码** - **大尺度结构网络**&#xff1a;将星系团映射为四色顶点&#xff0c;纤维状暗物质结构作为边&#xff0c;构建宇宙尺度…

蓝桥杯 封闭图形个数

蓝桥杯 封闭图形个数 题目 链接 解答 # 数字个数 n int(input()) # 数字 ls input().split() # 统计数字的圈数 o_nums {} for i, x in enumerate(ls):o_num 0for c in x:if int(c) in [0, 4, 6, 9]:o_num 1elif c 8:o_num 2o_nums[i] o_num # 字典根据圆圈数排序 …

基于javaweb的SpringBoot学生在线考试管理系统设计和实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

国产编辑器EverEdit - 超多样式设置

1 设置-编辑-样式 1.1 设置说明 1.1.1 折叠样式 默认为箭头&#xff0c;折叠样式选项如下&#xff1a; 箭头&#xff1a; 矩形和线条 五边形 圆形图标 1.1.2 光标样式 光标用于指示当前用户输入位置&#xff0c;光标样式选项如下&#xff1a; 默认 纤细 字宽 …

Linux - 线程控制

一、线程概念 1&#xff09;线程地址空间 线程与进程共享相同的虚拟地址空间&#xff0c;因此线程在访问内存时与进程没有本质的区别。但线程共享和独占的内存区域有不同的特点&#xff0c;理解这些特性对于正确使用线程至关重要。 1. 线程地址空间的组成 线程的地址空间是…

通过多线程分别获取高分辨率和低分辨率的H264码流

目录 一.RV1126 VI采集摄像头数据并同时获取高分辨率码流和低分辨率码流流程 ​编辑 1.1初始化VI模块&#xff1a; 1.2初始化RGA模块&#xff1a; 1.3初始化高分辨率VENC编码器、 低分辨率VENC编码器&#xff1a; 1.4 VI绑定高分辨率VENC编码器&#xff0c;VI绑定RGA模块…

部署RabbitMQ集群详细教程

部署RabbitMQ集群详细教程 下面是一份在 Ubuntu 环境下部署 RabbitMQ 集群的详细步骤说明&#xff0c;涉及主机名设置、Erlang & RabbitMQ 安装、管理插件启用、集群通信 Cookie 配置、节点加入集群、镜像队列策略设置以及集群验证等。为了演示方便&#xff0c;以下示例假…

【Linux】之【Bug】VMware 虚拟机开机 一直卡在黑屏左上角下划线闪烁界面

解决 参考&#xff1a; 解决Ubuntu20.04 开机黑屏光标闪烁进不去系统 Centos根目录100%解决思路 当前界面 ctrlaltf3-f6 暂时进入终端界面 df -h 查看发现根目录 磁盘空间已满 执行命令 查看当前目录占用内存明细 sudo du -h -x --max-depth1清理无用的大内存文件 或者安装…

webflux集成langchain4j基础版

伴随着大模型应用的兴起&#xff0c;webflux逐渐引起关注。为了以java的方式运行AI应用&#xff0c;让我们一起学习webflux集成langchain4j吧。 1. 项目依赖 首先&#xff0c;你需要在 pom.xml 中添加必要的依赖&#xff1a; <dependencies><!-- Spring WebFlux --…