深度分页介绍及优化建议

深度分页介绍

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

# 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 可能会选择全表扫描。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入剖析分布式事务:原理、方案与实战指南

引言&#xff1a;为什么分布式事务成为架构师的必修课&#xff1f; 在微服务架构大行其道的今天&#xff0c;单体应用被拆分成多个独立服务。当一次业务操作需要跨多个服务/数据库完成时&#xff0c;传统数据库事务的ACID特性不再适用。订单创建需要同时操作订单服务和库存服务…

NodeJS学习笔记

NodeJS软件安装 node环境安装&#xff1a; https://nodejs.org 安装好后的node通常在C:\Program Files\nodejs验证安装是否成功 node -v npm -v 进入REPL模式命令行模式 nodeNodeJS在REPL模式和编辑器使用 windos在dos下常用命令 windos命令&#xff1a; 1、cmd dos系统2、…

阿里云QwQ-32B模型发布:AI领域的新突破

在人工智能技术飞速发展的今天&#xff0c;每一次重大突破都可能改写行业的未来。近日&#xff0c;阿里云重磅发布通义千问 QwQ-32B 模型&#xff0c;这一消息如同一颗重磅炸弹&#xff0c;瞬间在 AI 领域掀起轩然大波&#xff0c;引发全球关注。 QwQ-32B 模型的惊艳之处&…

HarmonyOS NEXT开发实战:DevEco Studio中DeepSeek的使用

随着HarmonyOS Next的持续发布&#xff0c;鸿蒙系统对AI能力的支持显著增强。本文将深入探讨如何在鸿蒙应用中集成AI模型&#xff0c;结合接入DeepSeek&#xff0c;一起来探索开发鸿蒙原生应用的更多可能吧&#xff01; 第一步&#xff1a;安装使用 建议使用DevEco Studio 5.0…

VMware 安装部署RHEL9

目录 目标一&#xff1a;创建名为RHEL9_node2的虚拟机 1.环境搭建&#xff1a;VMware 2.下载RHEL9的ISO镜像&#xff08;官网可获取&#xff09; 3.打开VMware&#xff0c;新建虚拟机 3.1 自定义安装 3.2 默认操纵至下一步操作到稍后安装系统 3.3选择操作系统为linux以及…

基于Python实现的智能旅游推荐系统(Django)

基于Python实现的智能旅游推荐系统(Django) 开发语言:Python 数据库&#xff1a;MySQL所用到的知识&#xff1a;Django框架工具&#xff1a;pycharm、Navicat 系统功能实现 总体设计 系统实现 系统首页模块 统首页页面主要包括首页&#xff0c;旅游资讯&#xff0c;景点信息…

利用可变参数模板,可打印任意参数和参数值。(C++很好的调式函数)

很酷的应用&#xff1a; &#xff08;1&#xff09; 如何获取可变参数名 代码例子&#xff1a; #define _test(...) (test_t(#__VA_ARGS__, __VA_ARGS__))template<typename... Args> void test_t(const char* names, Args... args) {std::cout << names <<…

HarmonyOS 应用程序包结构 (编译态)

不同类型的Module编译后会生成对应的HAP、HAR、HSP等文件&#xff0c;开发态视图与编译态视图的对照关系如下&#xff1a; 从开发态到编译态&#xff0c;Module中的文件会发生如下变更&#xff1a; ets目录&#xff1a;ArkTS源码编译生成.abc文件。resources目录&#xff1a;A…

After Effects的图钉与关键帧动画

姜 子 博 引言 在数字媒体时代&#xff0c;动态图形和视觉效果在信息传播和表达中扮演着越来越重要的角色。After Effects 作为行业领先的软件&#xff0c;提供了丰富的工具和功能&#xff0c;帮助用户创作出令人惊叹的视觉作品。图钉工具和关键帧动画是 AE 中实现复杂动画效…

共享模型之管程(悲观锁)

共享模型之管程&#xff08;悲观锁&#xff09; 文章目录 共享模型之管程&#xff08;悲观锁&#xff09;一、常见线程安全的类二、对象头三、Monitor&#xff08;监视器 / 管程&#xff09;四、偏向锁偏向锁的实现原理撤销偏向锁 五、轻量级锁轻量级锁的释放 六、重量级锁七、…

upload-labs详解(13-20)文件上传分析

目录 upload-labs-env upload-labs-env第十三关 文件包含漏洞 代码 测试 上传一个.jpg图片 上传一个.png文件 上传一个.gif图片 upload-labs-env第十四关 代码 思路 upload-labs-env第十五关 代码 思路 upload-labs-env第十六关 代码 思路 测试 上传gif格式…

探索高性能AI识别和边缘计算 | NVIDIA Jetson Orin Nano 8GB 开发套件的全面测评

随着边缘计算和人工智能技术的迅速发展&#xff0c;性能强大的嵌入式AI开发板成为开发者和企业关注的焦点。NVIDIA近期推出的Jetson Orin Nano 8GB开发套件&#xff0c;凭借其40 TOPS算力、高效的Ampere架构GPU以及出色的边缘AI能力&#xff0c;引起了广泛关注。本文将从配置性…

字典树(trie树)详解

【本文概要】本文主要介绍了字典树的概念&#xff0c;字典树的一般算法&#xff0c;包括初始化&#xff0c;插入&#xff0c;查找等&#xff0c;最后举了比较典型的案例来辅助理解字典树这种特殊的数据结构。 1、什么是字典树 字典树&#xff0c;是一种特殊的树状数据结构&…

从CL1看生物计算机的创新突破与发展前景:技术、应用与挑战的多维度剖析

一、引言 1.1 研究背景与意义 随着科技的飞速发展&#xff0c;计算机技术已经成为推动现代社会进步的核心力量之一。从最初的电子管计算机到如今的大规模集成电路计算机&#xff0c;计算机的性能得到了极大的提升&#xff0c;应用领域也不断拓展。然而&#xff0c;传统计算机…

小兔鲜Vue3

counterStore里面包含着对象返回的东西。 getters就是conputer git initgit add .git commit -m " " jsconfig进行路径提示。vite.config.js进行实际路径转化。 第一个文件做好就是一个axios实例了&#xff0c;可以直接调用方法。 在第二个文件是实例.get 写好路…

驱动 AI 边缘计算新时代!高性能 i.MX 95 应用平台引领未来

智慧浪潮崛起&#xff1a;AI与边缘计算的时代 正悄然深植于我们的日常生活之中&#xff0c;无论是火热的 ChatGPT 与 DeepSeek 语言模型&#xff0c;亦或是 Meta 智能眼镜&#xff0c;AI 技术已经无形地影响着我们的生活。这股变革浪潮并未停歇&#xff0c;而是进一步催生了更高…

STM32之软件SPI

SPI传输更快&#xff0c;最大可达80MHz&#xff0c;而I2C最大只有3.4MHz。输入输出是分开的&#xff0c;可以同时输出输入。是同步全双工。仅支持一主多从。SS是从机选择线。每个从机一根。SPI无应答机制的设计。 注意&#xff1a;所有设备需要共地&#xff0c;时钟线主机输出&…

深度学习系列79:Text2sql调研

参考 https://github.com/topics/text-to-sql 这里是一些资源&#xff1a;https://github.com/eosphoros-ai/Awesome-Text2SQL/blob/main/README.zh.md 这里是综述文章&#xff1a;https://zhuanlan.zhihu.com/p/647249972 1. 数据集 Spider: 一个跨域的复杂text2sql数据集&a…

【Unity】 HTFramework框架(六十一)Project窗口文件夹锁定器

更新日期&#xff1a;2025年3月7日。 Github源码&#xff1a;[点我获取源码] Gitee源码&#xff1a;[点我获取源码] 索引 Project窗口文件夹锁定器框架文件夹锁定自定义文件夹锁定限制条件 Project窗口文件夹锁定器 在Project窗口中&#xff0c;文件夹锁定器能够为任何文件夹加…

nginx服务器实现上传文件功能_使用nginx-upload-module模块

目录 conf文件内容如下html文件内容如下上传文件功能展示 conf文件内容如下 #user nobody; worker_processes 1;error_log /usr/logs/error.log; #error_log /usr/logs/error.log notice; #error_log /usr/logs/error.log info;#pid /usr/logs/nginx.pid;even…