我说数据分页用Limit,面试官直接让我回去等消息

哈喽,大家哈,我是DanMu。最近我碰到了一个挺有趣的"小插曲",大概是这样的:现在有一个社交应用,在聊天界面中,用户可以通过下滑页面来不断加载历史消息。我当时想不就一个分页,这么简单的需求怎么能难倒我这个练习时长两年半的SQL boy,我直接一个啪一个limit上去直接就把这个问题解决了,写出来的SQL大概是这样的:

sql 复制代码
select * from message order by create_time desc limit n_page*50 50;

看了两眼,好像没有什么问题,然后就跑去忙别的了。

问题浮现

等到了下午,我忙完了手上的其他需求,准备开始对这个功能进行一个简单的压测,然后就可以美美的把代码提交,去博德之门里扔几把骰子。但是随着压测的进行,我发现这个接口的响应时间波动比较剧烈,一开始我还没当回事,认为是数据库压力太大导致的性能下 降,但是紧接着我就发现了这些性能较差的接口都具有较大的n_page,同时普遍耗时都在其他接口的10倍以上

这一下可就不能当做没看见了(悲伤痛哭)。算了,发现问题那就解决问题,于是经过我的一番查找,很快我就发现,原来这是全是深分页惹的祸!

深分页问题

那么什么是深分页问题呢? 在MySQLlimit中:limit 100,10MySQL会根据查询条件去存储引擎层找到前110条记录,然后在server层丢弃前100条记录取最后10条 这样先扫描完再丢弃的记录相当于白找,深分页问题指的就是这种场景(当limit的偏移量过大时会导致性能开销)。 那么要如何解决这个问题呢,现在比较多的实践是使用游标分页。

游标分页

游标分页的思想说起来也简单,既然采用limit 100,10这样的分页方式会导致MySQL从头开始扫描,从而扫描到没用的前100条记录,那么如果我们能够直接从第101条开始扫描,扫描10条,那么这个问题不就解决了。那么应该怎么实现呢,简单,在扫描时加一个条件不就得了

sql 复制代码
select * from message where create_time<上一次扫描到的最后一条记录的create_time order by create_time desc limit 10;

实际上游标翻页只是通过额外添加一个条件来改变MySQL扫描的起始位置。这也导致了游标翻页的使用必须要满足两个前提

  1. 需要一个列来记录上一次查询到的最大值,并且这个列要是有序的(比如我们上面用到的create_time)
  2. 每一次的查询会依赖上一次查询的最大值,因此,分页查询需要时连续的,不能进行跳页(比如查完第一页然后跳过第二页直接查第三页)

接下来让我们来做一个简单实验,验证一下游标分页的有效性。为了凸显效果,我们直接给表里整进去100万数据(这会花费很多时间,你可以自行减少记录数量)

plsql 复制代码
CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `empty_body` varchar(255) DEFAULT NULL,
  `create_time` datetime DEFAULT NOW(),
  PRIMARY KEY (`id`)
)ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='测试表';

DROP PROCEDURE IF EXISTS my_insert;

create procedure my_insert()
BEGIN
    declare i int default 1;
    set autocommit = 0;
    while i <= 1000000 do
            insert into diting.test
            (`empty_body`) values
                (concat('message',i));
            set i = i + 1;
        end while;
    set autocommit = 1;
END;

CALL my_insert();

接下来,让我们比较一下不同的查询方法所需要的时间

sql 复制代码
# 0.01 sec
select * from test limit 0,10;

# 深分页 0.28 sec
select * from test limit 1000000,10;

# 游标分页 0.00 sec
select * from test where id>1000000 limit 0,10;

可以看到,使用游标翻页的速度远远快于直接使用limit进行分页。

被忽略的问题

在上面我们证明了游标分页可以解决limit分页带来的性能问题,但是其实还有隐藏的,limit分页无法解决 的问题:消息重复问题 。让我们思考一下,如果我们在使用limit分页时,有新消息到来会怎么样。 没错!由于limit分页是根据和最新记录的差值来计算记录位置的,所以如果有新消息到达,那么在读取下一页的时候,就有可能读取到和上一页重复的消息。但是由于游标翻页采用的是固定的游标,即使有新消息到达也不会读取到重复的消息。

总结

游标分页非常适合于深分页的场景,能够大大减少需要的记录数量,但是由于游标依赖于上一次查询,所以无法实现跳页,并且只有作为游标的列有序的情况下才能使用游标。而且这个解决方案可以很容易的迁移到项目中任何需要分页的地方,非常适合作为面试时的技术亮点

点关注,不迷路

好了,以上就是这篇文章的全部内容了,如果你能看到这里,非常感谢你的支持! 如果你觉得这篇文章写的还不错, 求点赞 👍 求关注 ❤️ 求分享 👥 对暖男我来说真的 非常有用!!! 白嫖不好,创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见! 如果本篇博客有任何错误,请批评指教,不胜感激 !
最后推荐我的IM项目DiTinggithub.com/danmuking/D...),致力于成为一个初学者友好、易于上手的 IM 解决方案,希望能给你的学习、面试带来一点帮助,如果人才你喜欢,给个Star⭐叭!

相关推荐
uhakadotcom几秒前
BPF编程入门:使用Rust监控CPU占用
后端·面试·github
别惊鹊3 分钟前
(三)安装和使用Maven
java·maven
兢兢业业的小白鼠17 分钟前
Java高级JVM知识点记录,内存结构,垃圾回收,类文件结构,类加载器
java·开发语言·jvm·tomcat
uhakadotcom20 分钟前
GHSL-2024-252: Cloudflare Workers SDK 环境变量注入漏洞解析
后端·面试·github
uhakadotcom20 分钟前
GHSL-2024-264_GHSL-2024-265: 了解 AWS CLI 中的正则表达式拒绝服务漏洞 (ReDoS)
后端·面试·github
Asthenia041223 分钟前
Feign的协议和序列化是用的什么?
后端
uhakadotcom27 分钟前
了解Chainlit:简化AI应用开发的Python库
后端·面试·github
Codelinghu32 分钟前
26岁转型:研发骨干到技术Leader,我的管理心态转变与团队方法论
后端
落榜程序员36 分钟前
Java 基础-29-final关键字-详解
java·开发语言
拉丁解牛说技术39 分钟前
AI大模型进阶系列(01)AI大模型的主流技术 | AI对普通人的本质影响是什么?
后端·架构·openai