mysql分页页码越大,速度越慢(深度分页问题)

1. 什么是深度分页
深度分页问题的本质是在 MySQL 数据库中,通过 LIMIT 和 OFFSET 关键字进行分页时,MySQL 需要在每次查询时扫描整张表,直到找到当前页的数据。这种查询方式需要进行大量的磁盘 I/O 和内存操作,导致查询效率非常低下。当我们每次查询的记录数很小,但是查询次数很多时,就会产生大量的 I/O 操作,严重影响查询效率。
例如有个sql 

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


假设update_time 是有索引的,这个sql的执行流程:

通过普通二级索引树idx_update_time,过滤update_time条件,找到满足条件的记录ID。
通过ID,回到主键索引树,找到满足记录的行,然后取出展示的列(回表)
扫描满足条件的100010行,然后扔掉前100000行,返回。
假如 LIMIT 和 OFFSET 关键字同时进行使用的话,limit语句会先扫描offset+n行,然后再丢弃掉前offset行,返回后n行数据。也就是说limit 100000,10,就会扫描100010行,而limit 0,10,只扫描10行。这就是深度分页问题。

2. 优化方式
1. 通过子查询优化
因为以上的SQL,回表了100010次,实际上,我们只需要10条数据,也就是我们只需要10次回表其实就够了。因此,我们可以通过减少回表次数来优化。
其实我们只需要把条件转移到主键索引树就行了。

如果我们把查询条件,转移回到主键索引树,就可以减少回表次数了。转移到主键索引树查询的话,查询条件得改为主键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;


我们来看下执行计划


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

2. 标签记录法
limit 深分页问题的本质原因就是:偏移量(offset)越大,mysql就会扫描越多的行,然后再抛弃掉。这样就导致查询性能的下降。
其实我们可以采用标签记录法,就是标记一下上次查询到哪一条了,下次再来查的时候,从该条开始往下扫描。就好像看书一样,上次看到哪里了,你就折叠一下或者夹个书签,下次来看的时候,直接就翻到了。
假设上一次记录到100000,则SQL可以修改为:

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


这样的话,后面无论翻多少页,性能都会不错的,因为命中了id索引。

3. 使用标签记录法的例子
假设现在有表结构如下,并且有200万数据

CREATE TABLE account (
 id varchar(32) COLLATE utf8_bin NOT NULL COMMENT '主键',
 account_no varchar(64) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '账号'
 amount decimal(20,2) DEFAULT NULL COMMENT '金额'
 type varchar(10) COLLATE utf8_bin DEFAULT NULL COMMENT '类型A,B'
 create_time datetime DEFAULT NULL COMMENT '创建时间',
 update_time datetime DEFAULT NULL COMMENT '更新时间',
 PRIMARY KEY (id),
 KEY `idx_account_no` (account_no),
 KEY `idx_create_time` (create_time)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='账户表' 



需求是这样:获取最2021年的A类型账户数据,上报到大数据平台。
我们来使用标签记录法,避免深度分页问题:

//查询最小ID
String  lastId = accountDAO.queryMinId();

//查询最小ID对应的SQL
<select id="queryMinId" returnType=“java.lang.String”>
select MIN(id) 
from account
where create_time >='2021-01-01 00:00:00'
and type ='A'
</select>

//一页的条数
Integer pageSize = 100;

List<AcctountPO> list ;
do{
   list = listAccountByPage(lastId,pageSize);
   //标签记录法,记录上次查询过的Id
   lastId = list.get(list,size()-1).getId();
    //上报大数据
    postBigData(list);
}while(CollectionUtils.isNotEmpty(list));

<select id ="listAccountByPage">
  select * 
  from account 
  where create_time >='2021-01-01 00:00:00'
  and id > #{lastId}
  and type ='A'
  order by id asc  
  limit #{pageSize}
</select>


其中使用order by id 是为了应对id主键不连续的情况。

B+树的节点数据是有大小顺序的,所以limit 10,能保证读到db中10条逻辑大小相邻的数据,即不会漏掉数据
————————————————
版权声明:本文为CSDN博主「fFee-ops」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_21040559/article/details/130555007

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: MySQL分页越往后越慢的原因是因为MySQL在执行分页操作时,需要扫描整个数据集,并跳过前面的记录,直到找到需要返回的记录。因此,当页码越大,需要跳过的记录数就越多,查询的时间就会越长。为了解决这个问题,可以使用索引或者优化查询语句来减少扫描的记录数,从而提高查询速度。还可以使用缓存技术来减少数据库的访问次数,进一步提高性能。 ### 回答2: MySQL分页越往后越慢的原因有以下几点: 1. 数据增加:随着分页越往后,需要查询的总数据也随之增加。如果查询的表中包含大数据,即使使用索引加速查询,仍然需要读取更多的数据,导致查询速度变慢。 2. 执行排序:分页查询往往需要根据某一列进行排序,这个排序操作会消耗较多的资源和时间。随着分页越往后,需要排序的数据会变得越来越多,导致排序时间逐渐增加。 3. 索引失效:当分页查询的条件不符合索引的使用规则时,索引将会失效,导致查询变慢。分页查询的条件通常包括limit和offset,如果这些条件导致索引失效,MySQL将会扫描更多的数据页,降低查询效率。 4. 内存使用:MySQL分页查询需要将查询结果保存在内存中并返回给客户端,随着分页越往后,需要保存的数据也逐渐增加,可能导致内存不足,从而降低查询效率。 针对这些问题,可以采取以下措施来提高分页查询的性能: 1. 优化查询语句:尽可能减少查询的数据,只查询需要的字段,避免不必要的排序操作。同时,可以通过优化查询条件,让索引能够正常使用,提高查询效率。 2. 分批处理:将大的分页查询任务拆分成多个小的查询任务,分多次查询,减少每次查询的数据,降低负载,提高查询性能。 3. 使用缓存:如果分页查询的结果集是经常被使用的,可以考虑将结果集缓存在内存中,避免重复查询,提高访问速度。 4. 使用合适的硬件:分页查询需要大的计算和内存资源,使用高性能的硬件和配置合理的参数可以提高查询速度。 综上所述,MySQL分页越往后越慢的原因主要包括数据增加、执行排序、索引失效和内存使用等。通过优化查询语句、分批处理查询、使用缓存和合理配置硬件等措施可以提高分页查询的性能。 ### 回答3: MySQL分页越往后越慢是由于以下几个原因导致的。 首先,MySQL在查询大数据时,从硬盘上读取数据需要一定的时间。当偏移较小的时候,需要获取的数据相对较少,读取速度较快。然而,随着偏移的增加,需要跳过的数据越多,读取的数据越大,导致读取速度变慢。 其次,MySQL分页查询是通过LIMIT来实现的。当查询结果较小时,LIMIT语句的性能并不影响查询的速度。但是,当查询结果较大时,LIMIT语句会对查询进行额外的处理,通过跳过偏移来获取指定页面的数据。随着偏移的增加,LIMIT语句的处理时间也会增加,导致查询的速度降低。 此外,数据库查询还涉及到查询条件、索引等因素。当查询条件中包含多个约束条件时,MySQL需要对每个约束条件进行判断,并返回符合条件的结果。随着数据的增加,约束条件的判断时间也会增加,从而导致查询速度变慢。而索引的使用也可能受到偏移的影响,当偏移较大时,索引的效果减弱,查询速度变慢。 综上所述,MySQL分页查询越往后越慢是由于读取大数据的耗时、LIMIT处理时间增加、查询条件判断时间增加以及索引效果降低等因素共同作用的结果。为了提高分页查询的性能,可以考虑使用更有效的查询条件、优化数据库索引、合理设置偏移等措施。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值