我说数据分页用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⭐叭!

相关推荐
8***29318 分钟前
解决 Tomcat 跨域问题 - Tomcat 配置静态文件和 Java Web 服务(Spring MVC Springboot)同时允许跨域
java·前端·spring
CoderYanger12 分钟前
优选算法-栈:67.基本计算器Ⅱ
java·开发语言·算法·leetcode·职场和发展·1024程序员节
q***062932 分钟前
Tomcat的升级
java·tomcat
稚辉君.MCA_P8_Java37 分钟前
DeepSeek 插入排序
linux·后端·算法·架构·排序算法
多多*41 分钟前
Java复习 操作系统原理 计算机网络相关 2025年11月23日
java·开发语言·网络·算法·spring·microsoft·maven
青云交43 分钟前
Java 大视界 -- Java 大数据在智能物流无人配送车路径规划与协同调度中的应用
java·spark·路径规划·大数据分析·智能物流·无人配送车·协同调度
t***p9351 小时前
idea创建SpringBoot自动创建Lombok无效果(解决)
spring boot·后端·intellij-idea
d***81721 小时前
解决SpringBoot项目启动错误:找不到或无法加载主类
java·spring boot·后端
无限大61 小时前
RBAC模型:像电影院选座一样管理权限,告别"一个用户配一个权限"的噩梦
后端
间彧1 小时前
在CI/CD流水线中如何集成自动化的发布验证和熔断机制?
后端