当数据量达到百万级别的时候,分页该如何处理?

简介: 当数据量达到百万级别的时候,分页该如何处理?

最近遇到了这么一个情况,数据库里面的数据由于长期的堆积,导致数据量不断的上升,而后台的系统每次进行分页查询的时候,效率都会降低很多。后来查看了一下之后,发现此时的分页原理主要是采用了传统的物理分页 limit n,m 的方式。


为了方便演示,我特意创建了以下几张表进行实例演练:


表分别是商品表,用户表,用户选购商品记录表:


goods user g_u


三张表的关系比较简单,user的id和goods里面的id合并生成关联数据,存储在了g_u里面。三张数据库表的设计如下所示:


CREATE TABLE `goods` (
  `id` int(11) NOT NULL,
  `name` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
  `price` decimal(6,1) NOT NULL,
  `des` varchar(40) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `age` tinyint(3) NOT NULL,
  `sex` tinyint(1) NOT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100001 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `g_u` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `g_id` int(11) NOT NULL COMMENT '商品id',
  `u_id` int(11) NOT NULL COMMENT '用户id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2800001 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
复制代码


这个模拟的应用场景非常简单,用户和商品之间的关系维持在了一对多的关联中。为了方便进行后续的测试,我用jmeter批量创建了1900000条测试数据,模拟一次百万级的数据查询场景。


相应的数据脚本也已经存在百度云中了,需要的同学可以前往下载:


地址:


链接: https://pan.baidu.com/s/1BfddJ8MBtnpeiV84gNmClA 
提取码: 4kmp
复制代码


假设现在需求里面有这样的一个业务场景,需要我们对购买记录表里面的数据进行分页查询,那么对于常规的分页查询操作,常人会想到的方式可能是通过下述的语句:


SELECT * from g_u as gu ORDER BY id limit 1850000,100
复制代码


测试一下发现,查询的时间为:


网络异常,图片无法展示
|


当我们搜索的数据越靠后边的时候,搜索的速度就会越低下,因此这个时候,适当的创建索引就显得比较重要了。


首先我们来做一次explain的sql检测,检测结果为如下所示:


网络异常,图片无法展示
|


由于我们查询的时候,使用的是根据主键索引id进行排序,因此查询的时候key一项为

PRIMARY。


SELECT * FROM g_u WHERE id >=(SELECT id FROM g_u LIMIT 1850000,1) ORDER BY id  LIMIT 100
复制代码


此时查询有了一些许的提升,但是依旧查询缓慢


网络异常,图片无法展示
|


通过explain执行计划分析结果可见:


网络异常,图片无法展示
|


子查询用到了索引,外部查询用到了where的辅助索引


这个时候我们不妨可以试下通过利用主键id来提升我们的查询效率:


SELECT * FROM g_u as gu WHERE gu.id>($firstId+$pageSize*$pageSize)  limit 100
复制代码


查询的时间一下子大大缩短了许多:


网络异常,图片无法展示
|


通过explain分析一下该sql:


网络异常,图片无法展示
|


这里面,sql在运行的时候借助了主键索引的帮助,因此效率大大提升了。


但是这个时候,可能你会有这么一个疑惑。如果说数据的索引不是连续的该如何处理分页时候每页数据的完整性和一致性?


这里不妨可以试试另外的一种思路,通过建立一张第三方的表g_u_index表,将原本乱序的id存储在g_u_index中,在g_u_index一表中,我们可以通过该表有序的g_u_index.id来对应原本相应的无序的g_u.id。建表的sql语句如下所示:


CREATE TABLE `g_u_index` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `index` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_id_index` (`id`,`index`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1900024 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
复制代码


ps: 可以为id和index两者建立一套复合索引,提升查询的效率。


这里我们需要保证一点就是,g_u表中插入的数据顺序需要和g_u_index表中插入的顺序是一致的。然后查询分页指定的index时候可以这么来查:


SELECT g_u_index.index FROM g_u_index  WHERE id=($firstId+$pageSize*$pageSize)  limit 1
复制代码


通过执行explain分析后,结果变成如下所示:


网络异常,图片无法展示
|


查询时间为:0.001s


网络异常,图片无法展示
|


有了第三方表的帮助下,此时分页的sql优化可以调整为以下这种方式:


SELECT * FROM g_u as gu where gu.id>(
SELECT g_u_index.index FROM g_u_index  WHERE id=($firstId+$pageSize*$pageSize) limit 1
) limit 100
复制代码


通过构建了第三方表之后,数据的查询时间一下子大大缩减了:


网络异常,图片无法展示
|


查询的时候为了更加人性化,通常不需要显示这些无意义的id,需要的是商品名称和用户姓名,假设我们还是只采用最原始的无第三方表的方式进行查询的话,效率会比较底下:


SELECT gu.id,goods.`name`,`user`.username FROM g_u as gu ,goods ,`user` 
where goods.id=gu.g_id AND `user`.id=gu.u_id 
ORDER BY id limit 1500000,1000
复制代码


结果:


网络异常,图片无法展示
|


因此如果借助了第三方表查询的话,sql可以调整成下方这种类型:


SELECT goods.`name`,`user`.username FROM g_u as gu ,goods ,`user` 
where goods.id=gu.g_id AND `user`.id=gu.u_id 
and 
gu.id>=(
SELECT g_u_index.index FROM g_u_index  WHERE id=(9+1000*1900) limit 1
) limit 100
复制代码


查询的时间会大大减少:


网络异常,图片无法展示
|


通过explain执行计划分析之后,结果如下:




在实际的业务场景中,一张原来就有上百万数据的表要做出这样的id拆分,并且同步到第三方表的确实不太容易,这里推荐一种思路,可以借助阿里的中间件canal来实现对于数据库日志的订阅,然后自定义进行数据的同步操作。对于canal的讲解在我的这篇文章中也有讲述: blog.csdn.net/Danny_idea/…


对于sql的优化需要结合实际的业务需求来开展,总的来说,这部分还是需要有一定的实战演练才能变强。


常用的sql优化技巧小结:


1.数据量大的时候,应尽量避免全表扫描,应考虑在 where及 order by 涉及的列上建立索引,建索引可以大大加快数据的检索速度。


2.适当的使用Explain可以对sql进行相应的深入分析。


3.当只要一行数据时使用LIMIT 1。


4.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。


5.不要在 where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。


6.适当的时候采用覆盖索引可以提高查询的效率。

目录
相关文章
|
缓存 TensorFlow 算法框架/工具
conda 常用命令, Non-zero exit code,You will need to adjust your conda configuration
conda 常用命令, Non-zero exit code,You will need to adjust your conda configuration
|
存储 安全 数据建模
数据库——数据模型
数据库——数据模型
1032 0
|
移动开发 小程序
微信小程序web-view嵌入uni-app H5页面,通过H5页面传参给小程序进行转发分享页面,并通过点击转发出来的卡片,定向打开对应H5路径
微信小程序web-view嵌入uni-app H5页面,通过H5页面传参给小程序进行转发分享页面,并通过点击转发出来的卡片,定向打开对应H5路径
|
存储 SQL 数据可视化
Python自动化操作Excel
Python自动化操作Excel
447 0
Python自动化操作Excel
|
数据采集 算法
数据结构与算法—哈夫曼树详解与构造
给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
653 0
数据结构与算法—哈夫曼树详解与构造
|
JavaScript Unix 关系型数据库
Unix时间戳 POSIX时间 Unix时间
时间戳是自 1970 年 1 月 1 日(00:00:00 GMT)以来的秒数,也被称为 Unix 时间戳(Unix Timestamp)。Unix时间戳(Unix timestamp),或称Unix时间(Unix time)、POSIX时间(POSIX time),是一种时间表示方式,定义为从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数。
207039 2
|
5天前
|
云安全 人工智能 安全
AI被攻击怎么办?
阿里云提供 AI 全栈安全能力,其中对网络攻击的主动识别、智能阻断与快速响应构成其核心防线,依托原生安全防护为客户筑牢免疫屏障。
|
15天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~