聊聊面试必问的千万级数据库分页查询

写在文章开头

千万级别的MySQL单表查询算是近几年面试时碰到的一道比较棘手的问题,因为很多开发没有这方面的经验,所以最终回答都不是很好,所以笔者就以MySQL8作为实验数据库为读者演示一下笔者日常的处理思路和技巧。

Hi,我是sharkChili,是个不断在硬核技术上作死的java coder,是CSDN的博客专家,也是开源项目Java Guide的维护者之一,熟悉Java也会一点Go,偶尔也会在C源码边缘徘徊。写过很多有意思的技术博客,也还在研究并输出技术的路上,希望我的文章对你有帮助,非常欢迎你关注我的公众号:写代码的SharkChili ,获取笔者的联系方式备注 "加群" 和笔者的交流群进行深入交流。

百万级别数据查询实践

前置准备

为了方便演示笔者,这里拿出一张曾经作为批量插入的数据表,该表差不多有1000w左右的数据:

scss 复制代码
CREATE TABLE `batch_insert_test` (
  `id` int NOT NULL AUTO_INCREMENT,
  `fileid_1` varchar(100) DEFAULT NULL,
  `fileid_2` varchar(100) DEFAULT NULL,
  `fileid_3` varchar(100) DEFAULT NULL,
  `fileid_4` varchar(100) DEFAULT NULL,
  `fileid_5` varchar(100) DEFAULT NULL,
  `fileid_6` varchar(100) DEFAULT NULL,
  `fileid_7` varchar(100) DEFAULT NULL,
  `fileid_8` varchar(100) DEFAULT NULL,
  `fileid_9` varchar(100) DEFAULT NULL,
  `fileid_10` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25414 DEFAULT CHARSET=utf8mb3 COMMENT='测试批量插入,一行数据1k左右';

如何limit

按照分页查询公式,查询第N页的sql就是limit (n-1)*page , size,所以笔者对如下几个分页查询进行实验,不难看出,随着分页深度的增加,查询也变得十分耗时:

csharp 复制代码
select * from batch_insert_test bit2 limit 10,10;
select * from batch_insert_test bit2 limit 100,10;
select * from batch_insert_test bit2 limit 1000,10;
select * from batch_insert_test bit2 limit 10000,10;
select * from batch_insert_test bit2 limit 100000,10;
select * from batch_insert_test bit2 limit 1000000,10;
select * from batch_insert_test bit2 limit 5000000,10;

查看第500w页的数据10条,花费了将近1s:

csharp 复制代码
select * from batch_insert_test limit 5000000,10;

查看其执行计划,可以发现本次查询走了全表扫描,性能表现非常差劲:

sql 复制代码
id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows   |filtered|Extra|
--+-----------+-----+----------+----+-------------+---+-------+---+-------+--------+-----+
 1|SIMPLE     |batch_insert_test |          |ALL |             |   |       |   |9004073|   100.0|     |

所以我们需要对这些SQL进行改造,因为笔者这张表是以id作为主键的,所以我们可以很好的利用这一点,通过定位当前页的第一个id,然后通过这个id筛选对应页的数据,对应SQL如下所示,经过笔者的实验耗时大约在500ms左右:

bash 复制代码
select
 *
from
 batch_insert_test 
where
 id >=(select id from batch_insert_test bit2 limit 5000000,1)
 limit 10;

查看这条sql的执行计划可以发现,这条sql是直接通过索引直接定位id,避免走向叶子节点直接返回,再通过走索引的方式进行范围查询性能提升了不少。

sql 复制代码
id|select_type|table|partitions|type |possible_keys|key    |key_len|ref|rows |filtered|Extra                         |
--+-----------+-----+----------+-----+-------------+-------+-------+---+-----+--------+------------------------------+
 1|PRIMARY    |     |          |     |             |       |       |   |     |        |no matching row in const table|
 2|SUBQUERY   |bit2 |          |index|             |PRIMARY|4      |   |38677|   100.0|Using index                   |

limit多少

接下来就是limit数据量的选择了,有些读者可能为了方便直接在业务上进行改造,一次性查询大几十万数据给用户。 可以看到随着数据量的增加,查询耗时主键增大,所以读者在进行这方面考虑的时候务必要结合压测,根据自己业务上所能容忍的延迟涉及最大的pageSize,以笔者为例大约10w条以内的数据查询性能差异是不大的:

csharp 复制代码
select * from batch_insert_test bit2 limit 1000000,10;
select * from batch_insert_test bit2 limit 1000000,100;
select * from batch_insert_test bit2 limit 1000000,1000;
select * from batch_insert_test bit2 limit 1000000,10000;
select * from batch_insert_test bit2 limit 1000000,100000;
select * from batch_insert_test bit2 limit 1000000,1000000;
select * from batch_insert_test bit2 limit 1000000,10000000;

其他注意事项

还有一点细节上的优化,MySQL的基本单位是页,所以每次查询都是以页为单位进行查询,所以高效的查询也要求我们用尽可能少的块查到存储尽可能多的数据,所以查询时我们建议没有用到的列就不要查询来了。

以笔者为例,只需用到3个字段,则直接将*改为了id,fileid_1 ,fileid_4

bash 复制代码
select
 id,fileid_1 ,fileid_4 
from
 batch_insert_test bit2
where
 id >(select id from batch_insert_test bit2 limit 5000000,1)
 limit 10;

小结

来简单小结一下,本文通过一张大表结合一个分页查询的场景为读者演示的大表分页查询的技巧,整体来说,针对大表查询时,我们的SQL优化要遵循以下几点:

  1. 尽可能利用索引,确保用最小的开销得到索引。
  2. 结合业务场景和服务器性能压测出最合适的limit数据量。
  3. 尽量不要查询没必要的列。

我是sharkchiliCSDN Java 领域博客专家开源项目---JavaGuide contributor ,我想写一些有意思的东西,希望对你有帮助,如果你想实时收到我写的硬核的文章也欢迎你关注我的公众号:写代码的SharkChili ,取笔者的联系方式备注 "加群" 和笔者的交流群进行深入交流。

参考

面试官:一千万的数据,你是怎么查询的? :mp.weixin.qq.com/s/zhmVw1C5B...

本文使用 markdown.com.cn 排版

相关推荐
0zxm7 分钟前
06 - Django 视图view
网络·后端·python·django
m0_748257187 分钟前
Spring Boot FileUpLoad and Interceptor(文件上传和拦截器,Web入门知识)
前端·spring boot·后端
小_太_阳1 小时前
Scala_【1】概述
开发语言·后端·scala·intellij-idea
智慧老师1 小时前
Spring基础分析13-Spring Security框架
java·后端·spring
搬码后生仔3 小时前
asp.net core webapi项目中 在生产环境中 进不去swagger
chrome·后端·asp.net
凡人的AI工具箱3 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite
Lx3523 小时前
Pandas数据重命名:列名与索引为标题
后端·python·pandas
小池先生3 小时前
springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失
java·spring boot·后端
百罹鸟4 小时前
【vue高频面试题—场景篇】:实现一个实时更新的倒计时组件,如何确保倒计时在页面切换时能够正常暂停和恢复?
vue.js·后端·面试
小蜗牛慢慢爬行5 小时前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate