文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

MySQL深度分页

2023-10-02 22:05

关注

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的执行流程:

  1. 通过普通二级索引树idx_update_time,过滤update_time条件,找到满足条件的记录ID。
  2. 通过ID,回到主键索引树,找到满足记录的行,然后取出展示的列(回表)
  3. 扫描满足条件的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类型账户数据,上报到大数据平台。
我们来使用标签记录法,避免深度分页问题:

//查询最小IDString  lastId = accountDAO.queryMinId();//查询最小ID对应的SQL<select id="queryMinId" returnType=java.lang.String>select MIN(id) from accountwhere 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条逻辑大小相邻的数据,即不会漏掉数据

来源地址:https://blog.csdn.net/qq_21040559/article/details/130555007

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯