MySQL深分页,limit 100000,10 优化

文章目录

  • 一、limit深分页为什么会变慢
  • 二、优化方案
    • 2.1 通过子查询优化(覆盖索引)
      • 回顾B+树结构
      • 覆盖索引
      • 把条件转移到主键索引树
    • 2.2 INNER JOIN 延迟关联
    • 2.3 标签记录法(要求id是有序的)
    • 2.4 使用between...and...

我们日常做分页需求时,一般会用limit实现,但是当偏移量特别大的时候,查询效率就变得低下。本文将分4个方案,讨论如何优化MySQL百万数据的深分页问题.

参考 实战!聊聊如何解决MySQL深分页问题

一、limit深分页为什么会变慢

表结构

CREATE TABLE account (id int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',name varchar(255) DEFAULT NULL COMMENT '账户名',balance int(11) DEFAULT NULL COMMENT '余额',create_time datetime NOT NULL COMMENT '创建时间',update_time datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (id),KEY idx_name (name),KEY idx_update_time (update_time) //索引
) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='账户表';

执行的深分页SQL为

select id,name,balance from account where update_time> '2020-09-19' limit 100000,10;

这个SQL的执行时间如下:

img

执行完需要0.742秒,深分页为什么会变慢呢?如果换成 limit 0,10,只需要0.006秒哦

img

我们先来看下这个SQL的执行流程

  1. 通过普通二级索引树idx_update_time,过滤update_time条件,找到满足条件的记录ID。

  2. 通过ID,回到主键索引树,找到满足记录的行,然后取出展示的列(回表

  3. 扫描满足条件的100010行,然后扔掉前100000行,返回。

    (每一条select语句都会从1遍历至当前位置,若跳转到第10000页,则会遍历100000条记录)

在这里插入图片描述

执行计划如下: img

SQL变慢原因有两个

  1. limit语句会先扫描offset+n行,然后再丢弃掉前offset行,返回后n行数据。也就是说limit 100000,10,就会扫描100010行,而limit 0,10,只扫描10行。
  2. limit 100000,10 扫描更多的行数,也意味着回表更多的次数。

二、优化方案

2.1 通过子查询优化(覆盖索引)

因为以上的SQL,回表了100010次,实际上,我们只需要10条数据,也就是我们只需要10次回表其实就够了。因此,我们可以通过减少回表次数来优化。

回顾B+树结构

如何减少回表次数呢?我们先来复习下B+树索引结构

InnoDB中,索引分主键索引(聚簇索引)和二级索引

  • 主键索引,叶子节点存放的是整行数据
  • 二级索引,叶子节点存放的是主键的值

img

覆盖索引

覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能。

如何确定数据库成功使用了覆盖索引呢? —— 当发起一个索引覆盖查询时,在explain的extra列可以看到using index的信息

在这里插入图片描述

可以看到Extra中的Using index,表明我们成功使用了覆盖索引

把条件转移到主键索引树

如果我们把查询条件,转移回到主键索引树,那就不就可以减少回表次数啦。转移到主键索引树查询的话,查询条件得改为主键id了,之前SQL的update_time这些条件咋办呢?抽到子查询那里嘛~

子查询那里怎么抽的呢?因为二级索引叶子节点是有主键ID的,所以我们直接根据update_time来查主键ID即可,同时我们把 limit 100000的条件,也转移到子查询,完整SQL如下:

select id,name,balance FROM account where id >= (select a.id from account a where a.update_time >= '2020-09-19' limit 100000, 1) LIMIT 10; -- (可以加下时间条件到外面的主查询)

查询效果一样的,执行时间只需要0.038秒! 0.742秒 ——> 0.038秒

img

我们来看下执行计划 img

由执行计划得知,子查询 table a查询是用到了idx_update_time索引。首先在索引上拿到了聚集索引的主键ID,省去了回表操作,然后第二查询直接根据第一个查询的ID往后再去查10个就可以了!

img

所谓的覆盖索引就是从普通索引树中就能查到的想要数据,而不需要通过回表从主键索引中查询其他列,能够显著提升性能。

因此,这个方案是可以的~

2.2 INNER JOIN 延迟关联

延迟关联的优化思路,跟子查询的优化思路其实是一样的:都是把条件转移到主键索引树,然后减少回表。不同点是,延迟关联使用了inner join代替子查询。

优化后的SQL如下:

SELECT  acct1.id,acct1.name,acct1.balance FROM account acct1 INNER JOIN (SELECT a.id FROM account a WHERE a.update_time >= '2020-09-19' ORDER BY a.update_time LIMIT 100000, 10) AS  acct2 on acct1.id= acct2.id;

查询效果也是杠杆的,只需要0.034秒

img

执行计划如下:

img

查询思路就是,先通过idx_update_time二级索引树查询到满足条件的主键ID,再与原表通过主键ID内连接,这样后面直接走了主键索引了,同时也减少了回表。

2.3 标签记录法(要求id是有序的)

limit 深分页问题的本质原因就是:偏移量(offset)越大,mysql就会扫描越多的行,然后再抛弃掉。这样就导致查询性能的下降

其实我们可以采用标签记录法,就是标记一下上次查询到哪一条了,下次再来查的时候,从该条开始往下扫描。就好像看书一样,上次看到哪里了,你就折叠一下或者夹个书签,下次来看的时候,直接就翻到啦

select id,name,balance from account limit 1000000,10;

假设上一次记录到100000,则SQL可以优化为:

select id,name,balance FROM account where id > 100000 order by id limit 10;

这样的话,后面无论翻多少页,性能都会不错的,因为命中了id索引。但是你,这种方式有局限性:要求id连续的、并且有序

在有序的条件下,也可以使用比如创建时间等其他字段来代替主键id,但是前提是这个字段是建立了索引的。

id不是连续,我们可以通过order by让它连续

总之,使用条件过滤的方式来优化 limit 是有诸多限制的,一般还是推荐使用覆盖索引的方式来优化。

2.4 使用between…and…

很多时候,可以将limit查询转换为已知位置的查询,这样MySQL通过范围扫描between...and,就能获得到对应的结果。

select id,name,balance from account limit 1000000,10;

如果知道边界值为100000,100010后,就可以这样优化:

select id,name,balance FROM account where id between 100000 and 100010 order by id desc;

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

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

相关文章

拿捏红黑树(C++)

文章目录 前言一、红黑树介绍二、插入操作三、验证红黑树四、红黑树与AVL性能比较与应用五、总体代码总结 前言 我们之前介绍了一种AVL的高阶数据结构,在本篇文章中,我们将会介绍一种与AVL旗鼓相当的数据结构–红黑树。 我们并且会对它的部分接口进行模…

Autoxjs 实践-Spring Boot 集成 WebSocket

概述 最近弄了福袋工具,由于工具运行中,不好查看福袋结果,所以我想将福袋工具运行数据返回到后台,做数据统计、之后工具会越来越多,就弄了个后台,方便管理。 实现效果 WebSocket? websocket是…

动态规划(01背包+并查集)

P1455 搭配购买 题意:就是说有n朵云,每朵云有自己的价钱(重量)和价值(价值),还有我自己现在有钱的数目(背包),然后还告诉你,哪几朵云是属于捆绑销…

“独特团购策略引领小程序商城一月狂赚600万“

你是否曾经对那些富有创意且成功的商业模式心生羡慕,最终它们通过非凡的业绩证明了自身的价值?今日,我要分享的是一个独特的小程序商城案例,它凭借一种别出心裁的团购策略,在短短一个月内实现了超过600万的营收&#x…

LeetCode 56 合并区间

本题中可以学到的比较重要的方法 lambda表达式定义自定义比较器Comparator Arrays.sort(intervals,(v0,v1)->{return v0[0] - v1[0];}); (附 : 这种形式也适合于优先队列创建时的自定义比较器定义) 比如&#xff1a; PriorityQueue<Integer> minTop new Priorit…

JAVA小案例-输出100-150中能被3整除的数,每5个换行

JAVA小案例-输出100-150中能被3整除的数&#xff0c;每5个换行 代码如下&#xff1a; public class Continue {/*** continue练习&#xff0c;输出100-150中能被3整除的数&#xff0c;每5个换行* param args*/public static void main(String[] args) {int count 0;//计数器…

【kubernetes】探索k8s集群的存储卷、pvc和pv

目录 一、emptyDir存储卷 1.1 特点 1.2 用途 1.3部署 二、hostPath存储卷 2.1部署 2.1.1在 node01 节点上创建挂载目录 2.1.2在 node02 节点上创建挂载目录 2.1.3创建 Pod 资源 2.1.4访问测试 2.2 特点 2.3 用途 三、nfs共享存储卷 3.1特点 3.2用途 3.3部署 …

ARM32开发--GPIO输入模式

知不足而奋进 望远山而前行 目录 文章目录 前言 浮空输入 上拉输入 下拉输入 模拟输入 总结 前言 在数字电路设计和嵌入式系统开发中&#xff0c;理解输入信号的处理方式对确保系统稳定性和可靠性至关重要。不同的输入处理方式包括上拉输入、下拉输入、浮空输入和模拟输…

解决JSON.stringify 方法在序列化 BigInt 类型时的错误

今天学nest时&#xff0c;使用apifox发送请求获取数据&#xff0c;结果还一直报错&#xff0c;而且还是我从未见过的 Do not know how to serialize a BigInt at JSON.stringify (<anonymous>) at stringify&#xff0c; 我都是跟着人家敲的&#xff0c;我就纳闷了&…

06Docker-Compose和微服务部署

Docker-Compose 概述 Docker Compose通过一个单独的docker-compose.yml模板文件来定义一组相关联的应用容器&#xff0c;帮助我们实现多个相互关联的Docker容器的快速部署 一般一个docker-compose.yml对应完整的项目,项目中的服务和中间件对应不同的容器 Compose文件实质就…

高德面试:为什么Map不能插入null?

在 Java 中&#xff0c;Map 是属于 java.util 包下的一个接口&#xff08;interface&#xff09;&#xff0c;所以说“为什么 Map 不能插入 null&#xff1f;”这个问题本身问的不严谨。Map 部分类关系图如下&#xff1a; 所以&#xff0c;这里面试官其实想问的是&#xff1a;为…

【Python系列】Python 方法变量参数详解

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

RetroMAE-文本embedding算法

1)输入文本经掩码操作后由编码器&#xff08;Encoder&#xff09;映射为隐空间中的语义向量&#xff1b;而后解码器&#xff08;Decoder&#xff09;借助语义向量将另一段独立掩码的输入文本还原为原始的输入文本 2)编码器的掩码率为15%-30%&#xff1b;解码器的掩码率为50%-70…

【工具】批量SKU生成器

一个用户加我&#xff0c;要我帮忙写一个生成SKU的工具&#xff0c;他希望可以自定义生成的选项&#xff0c;可以批量生成。我到网上找了好久也没有找到好用的&#xff0c;就花了一下午写了这个生成sku的功能 工具支持批量生成SKU&#xff0c;支持自定义配置项&#xff0c;支持…

多表连接查询和子查询

一、连接查询 连接查询是SQL语言最强大的功能之一&#xff0c;它可以执行查询时动态的将表连接起来&#xff0c;然后从中查询数据。 1.1、连接两表的方法 在SQL中连接两表可以有两种方法&#xff0c;一种是无连接规则连接&#xff0c;另一种是有连接规则连接。 无连接规则连…

Spring Boot 整合 spring-boot-starter-mail 实现邮件发送和账户激活

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

C#WPF数字大屏项目实战08--生产量/良品统计

1、区域划分 生产量/良品统计这部分位于第二列的第二行 2、livechart拆线图 定义折线图,如下: <lvc:CartesianChart> <lvc:CartesianChart.Series> <!--设置Series的类型为 Line 类型, 该类型提供了一些折线图的实现--> <lvc:LineSeries/>…

性能狂飙:SpringBoot应用优化实战手册

在数字时代&#xff0c;速度就是生命&#xff0c;性能就是王道&#xff01;《极速启航&#xff1a;SpringBoot性能优化的秘籍》带你深入SpringBoot的内核&#xff0c;探索如何打造一个飞速响应、高效稳定的应用。从基础的代码优化到高级的数据库连接池配置&#xff0c;再到前端…

数据库与数据库管理系统 MySQL的安装 SQL语言学习:DDL、DML

day51 数据库 数据库&#xff08;database&#xff09;就是一个存储数据的仓库。为了方便数据的存储和管理&#xff0c;它将数据按照特定的规律存储在磁盘上。 通过数据库管理系统&#xff0c;可以有效地组织和管理存储在数据库中的数据&#xff0c;如数据库管理系统MySQL 数据…

Python-3.12.0文档解读-内置函数repr()详细说明+记忆策略+常用场景+巧妙用法+综合技巧

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 详细说明 概述 详细描述 自定义类的行为 使用示例 异常处理 注意事项 总结 记…