MySQL深度分页(千万级数据量如何快速分页)

 更新时间:2021年07月25日 10:31:02   作者:少侠露飞  
后端开发中经常需要分页展示,个时候就需要用到MySQL的LIMIT关键字。LIMIT在数据量大的时候极可能造成的一个问题就是深度分页。本文就介绍一下解决方法,感兴趣的可以了解一下

前言

后端开发中为了防止一次性加载太多数据导致内存、磁盘IO都开销过大,经常需要分页展示,这个时候就需要用到MySQL的LIMIT关键字。但你以为LIMIT分页就万事大吉了么,Too young,too simple啊,LIMIT在数据量大的时候极可能造成的一个问题就是深度分页。

案例

这里我以显示电商订单详情为背景举个例子,新建表如下:

CREATE TABLE `cps_user_order_detail` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` varchar(32) NOT NULL DEFAULT '' COMMENT '用户ID',
  `order_id` bigint(20) DEFAULT NULL COMMENT '订单id',
  `sku_id` bigint(20) unsigned NOT NULL COMMENT '商品ID',
  `order_time` datetime DEFAULT NULL COMMENT '下单时间,格式yyyy-MM-dd HH:mm:ss',
   PRIMARY KEY (`id`),
   KEY `idx_time_user` (`order_time`,`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='用户订单详情';

然后手动向表里插入120W条数据。
现在有个需求:分页展示用户的订单详情,按照下单时间倒序。
表结构精简了,需求也简单。于是哗哗哗的写完代码,提测上线了。早期运行一切正常,可随着订单量的不断增大,发现系统越发的缓慢,还时不时报出几个慢查询
这个时候你就该想到是LIMIT偏移的问题了,没错,不是你的SQL不够优美,就是MySQL自身的机制。
这里我就简单以两条SQL为例,如下图,分别是从100和100W的位置偏移分页,可以看到时间相差很大。这还不算其它数据运算和处理的时间,单一条SQL的查询就耗时一秒以上,在对用户提供的功能里这是不能容忍的(电商里经常要求一个接口的RT不超过200ms)。

这里我们再看下执行计划,如下图所示:

在此先介绍一下执行计划Extra列可能出现的值及含义:

  1. Using where:表示优化器需要通过索引回表查询数据。
  2. Using index:即覆盖索引,表示直接访问索引就足够获取到所需要的数据,不需要通过索引回表,通常是通过将待查询字段建立联合索引实现。
  3. Using index condition:在5.6版本后加入的新特性,即大名鼎鼎的索引下推,是MySQL关于减少回表次数的重大优化。
  4. Using filesort:文件排序,这个一般在ORDER BY时候,数据量过大,MySQL会将所有数据召回内存中排序,比较消耗资源。

再看看上图,同样的语句,只因为偏移量不同,就造成了执行计划的千差万别(且容我小小的夸张一下)。第一条语句LIMIT 100,6type列的值是range,表示范围扫描,性能比ref差一个级别,但是也算走了索引,并且还应用了索引下推:就是说在WHERE之后的下单时间删选走了索引,并且之后的ORDER BY也是根据索引下推优化,在执行WHERE条件筛选时同步进行的(没有回表)。
而第二条语句LIMIT 1000000,6压根就没走索引,type列的值是ALL,显然是全表扫描。并且Extra列字段里的Using where表示发生了回表,Using filesort表示ORDER BY时发生了文件排序。所以这里慢在了两点:一是文件排序耗时过大,二是根据条件筛选了相关的数据之后,需要根据偏移量回表获取全部值。无论是上面的哪一点,都是LIMIT偏移量过大导致的,所以实际开发环境经常遇到非统计表量级不得超过一百万的要求。

优化

原因分析完了,那么LIMIT深度分页在实际开发中怎么优化呢?这里少侠给两点方案。
一是通过主键索引优化。什么意思呢?就是把上面的语句修改成:

SELECT * FROM cps_user_order_detail d WHERE d.id > #{maxId} AND d.order_time>'2020-8-5 00:00:00' ORDER BY d.order_time LIMIT 6;

如上代码所示,同样也是分页,但是有个maxId的限制条件,这个是什么意思呢,maxId就是上一页中的最大主键Id。所以采用此方式的前提:1)主键必须自增不能是UUID并且前端除了传基本分页参数pageNo,pageSize外,还必须把每次上一页的最大Id带过来,2)该方式不支持随机跳页,也就是说只能上下翻页。如下图所示是某知名电商中的实际页面。

二是通过Elastic Search搜索引擎优化(基于倒排索引),实际上类似于淘宝这样的电商基本上都是把所有商品放进ES搜索引擎里的(那么海量的数据,放进MySQL是不可能的,放进Redis也不现实)。但即使用了ES搜索引擎,也还是有可能发生深度分页的问题的,这时怎么办呢?答案是通过游标scroll。关于此点这里不做深入,感兴趣的可以做研究。

小结

写这篇博客是因为前段时间在开发中真实经历到了,并且之前在字节面试中确实也和面试官探讨了一番。知道LIMIT的限制以及优化,在面试中能提到是加分项,不能说到MySQL优化就是建索引,调整SQL(实际上在真实开发中这两种优化方案的成效微乎其微)。毕竟MySQL优化那么牛X的话,就不会有那么多中间件产生了。

到此这篇关于MySQL深度分页(千万级数据量如何快速分页)的文章就介绍到这了,更多相关MySQL 深度分页内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MYSQL数据库GTID实现主从复制实现(超级方便)

    MYSQL数据库GTID实现主从复制实现(超级方便)

    这篇文章主要介绍了MYSQL数据库GTID实现主从复制实现(超级方便),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • 创建一个实现Disqus评论模版的MySQL模型

    创建一个实现Disqus评论模版的MySQL模型

    这篇文章主要介绍了创建一个实现Disqus评论模版的MySQL模型,Disqus网站的数据库采用PostgreSQL,而作者则以MySQL来实现,需要的朋友可以参考下
    2015-06-06
  • mysql查询如何去掉多余零

    mysql查询如何去掉多余零

    这篇文章主要介绍了mysql查询如何去掉多余零问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • 详解MySql的慢查询分析及开启慢查询日志

    详解MySql的慢查询分析及开启慢查询日志

    本篇文章主要介绍了详解MySql的慢查询分析及开启慢查询日志,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-03-03
  • MySQL时间戳与日期格式的相互转换

    MySQL时间戳与日期格式的相互转换

    在MySQL数据库中,时间戳和日期格式是常用的数据类型,在MySQL中,我们可以使用函数还相互转换时间戳和日期格式,下面我将详细的给大家介绍如何进行转换,并提供相应的代码示例,感兴趣的小伙伴跟着小编一起来看看吧
    2024-01-01
  • 对比MySQL中int、char以及varchar的性能

    对比MySQL中int、char以及varchar的性能

    在本篇文章中我们给大家分享了关于MySQL中int、char以及varchar的性能对比的相关内容,有兴趣的朋友们学习下。
    2018-10-10
  • MySQL limit分页大偏移量慢的原因及优化方案

    MySQL limit分页大偏移量慢的原因及优化方案

    这篇文章主要介绍了MySQL limit分页大偏移量慢的原因及优化方案,帮助大家更好的理解和使用MySQL数据库,感兴趣的朋友可以了解下
    2020-11-11
  • MySQL系统库之information_schema的实现

    MySQL系统库之information_schema的实现

    information_schema用于存储数据库元数据,本文主要介绍了MySQL系统库之information_schema的实现,具有一定的参考价值,感兴趣的可以了解一下
    2023-11-11
  • 解决mysql错误:Subquery returns more than 1 row问题

    解决mysql错误:Subquery returns more than 1 row问题

    这篇文章主要介绍了解决mysql错误:Subquery returns more than 1 row问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • MySQL回滚日志(undo log)的作用和使用详解

    MySQL回滚日志(undo log)的作用和使用详解

    undo log是innodb引擎的一种日志,在事务的修改记录之前,会把该记录的原值先保存起来再做修改,以便修改过程中出错能够恢复原值或者其他的事务读取,这篇文章主要给大家介绍了关于MySQL回滚日志(undo log)的作用和使用的相关资料,需要的朋友可以参考下
    2022-04-04

最新评论