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


Posted in MySQL onJuly 25, 2021

前言

后端开发中为了防止一次性加载太多数据导致内存、磁盘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)。

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

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

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

在此先介绍一下执行计划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)该方式不支持随机跳页,也就是说只能上下翻页。如下图所示是某知名电商中的实际页面。

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

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

小结

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

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

MySQL 相关文章推荐
MySQL优化之如何写出高质量sql语句
May 17 MySQL
修改MySQL的默认密码的四种小方法
May 26 MySQL
MySQL 亿级数据导入导出及迁移笔记
Jun 18 MySQL
MySQL系列之八 MySQL服务器变量
Jul 02 MySQL
MySQL系列之九 mysql查询缓存及索引
Jul 02 MySQL
MySQL悲观锁与乐观锁的实现方案
Nov 02 MySQL
分享mysql的current_timestamp小坑及解决
Nov 27 MySQL
关于k8s环境部署mysql主从的问题
Mar 13 MySQL
MySQL优化常用的19种有效方法(推荐!)
Mar 17 MySQL
详解Mysql数据库平滑扩容解决高并发和大数据量问题
May 25 MySQL
MySQL transaction事务安全示例讲解
Jun 21 MySQL
mysql sock 文件解析及作用讲解
Jul 15 MySQL
ORM模型框架操作mysql数据库的方法
mysql 直接拷贝data 目录下文件还原数据的实现
MySQL索引是啥?不懂就问
MySQL 四种连接和多表查询详解
MySQL 聚合函数排序
MySQL 那些常见的错误设计规范,你都知道吗
Jul 16 #MySQL
MySQL 5.7常见数据类型
You might like
攻克CakePHP系列三 表单数据增删改
2008/10/22 PHP
PHP 配置open_basedir 让各虚拟站点独立运行
2009/11/12 PHP
Look And Say 序列php实现代码
2011/05/22 PHP
php array_push()数组函数:将一个或多个单元压入数组的末尾(入栈)
2011/07/12 PHP
使用dump函数,给php加断点测试
2013/06/25 PHP
PHP中几个可以提高运行效率的代码写法、技巧分享
2014/08/21 PHP
CodeIgniter框架URL路由总结
2014/09/03 PHP
thinkphp区间查询、统计查询与SQL直接查询实例分析
2014/11/24 PHP
joomla组件开发入门教程
2016/05/04 PHP
PHP判断用户是否已经登录(跳转到不同页面或者执行不同动作)
2016/09/22 PHP
php指定长度分割字符串str_split函数用法示例
2017/01/30 PHP
JMenuTab简单使用说明
2008/03/13 Javascript
Mootools 1.2教程 定时器和哈希简介
2009/09/15 Javascript
jQuery入门第一课 jQuery选择符
2010/03/14 Javascript
addEventListener和attachEvent二者绑定的执行函数中的this不相同
2012/12/09 Javascript
jquery.cookie实现的客户端购物车操作实例
2015/12/24 Javascript
分享JavaScript监听全部Ajax请求事件的方法
2016/08/28 Javascript
JS实现禁止鼠标右键的功能
2016/10/15 Javascript
如何给ss bash 写一个 WEB 端查看流量的页面
2017/03/23 Javascript
vue通信方式EventBus的实现代码详解
2019/06/10 Javascript
[18:32]DOTA2 HEROS教学视频教你分分钟做大人-谜团
2014/06/12 DOTA
跨平台python异步回调机制实现和使用方法
2013/11/26 Python
Python数据分析中Groupby用法之通过字典或Series进行分组的实例
2017/12/08 Python
Python实现按当前日期(年、月、日)创建多级目录的方法
2018/04/26 Python
Python创建普通菜单示例【基于win32ui模块】
2018/05/09 Python
关于numpy中eye和identity的区别详解
2019/11/29 Python
Python打包模块wheel的使用方法与将python包发布到PyPI的方法详解
2020/02/12 Python
PyCharm2019.3永久激活破解详细图文教程,亲测可用(不定期更新)
2020/10/29 Python
浅谈pc和移动端的响应式的使用
2019/01/03 HTML / CSS
wordpress添加Html5的表单验证required方法小结
2020/08/18 HTML / CSS
日本著名的服饰鞋帽综合类购物网站:MAGASEEK
2019/01/09 全球购物
幼儿园春游活动方案
2014/01/19 职场文书
省文明单位申报材料
2014/05/08 职场文书
大型公益活动策划方案
2014/08/20 职场文书
二手车转让协议书
2015/01/29 职场文书
公司员工手册范本
2015/05/14 职场文书