老弟想自己做个微信,被我一个问题劝退了。。

大家好,我是程序员鱼皮。最近老弟小阿巴放暑假,想找点事情做,于是就来问我:老鲏,我想做个练手项目,有没有什么好的建议?

我说:练手项目的话,就做个自己感兴趣的呗,想加什么功能就加什么,做起来会更舒服~

小阿巴:Emm,我感兴趣的太多了,有没有推荐啊?

我说:那就想想自己经常使用的网站或 APP,选个对业务流程相对熟悉的。

小阿巴思考片刻,一拍脑袋:对啊,我天天用微信,那我就做个微信吧!说不定之后大家都在用我做的软件聊天呢?

我一听,不禁暗自惊叹,没想到小伙子年纪轻轻,野心很大啊!

我说:想法不错,但想做个微信这样的 IM(即时通讯)项目,可没有那么简单,你有什么实现思路么?说来听听?

小阿巴:微信的核心功能是收发消息,我可以把用户 A 发送的消息保存到数据库中,用户 B 进入聊天界面时,从数据库查询出发给他的消息就行。

我一听这个回答,就知道以小阿巴目前的水平,想做出微信是不太可能了。。。

我问:Emm,暂且不考虑用户体验和性能,我们就先实现基础功能吧,你会怎么让用户查看自己的历史消息呢?

小阿巴思考片刻,然后嘴角微微上扬,露出狡黠的笑容:你是不是以为我会说一次性把所有历史消息全部查出来?可惜啊老鲏,你把我想的太天真了,用户可能有成百上千条历史消息,全量加载会很慢,所以我必然会使用 分页 来查询!

我说:行,那你打算怎么分页呢?

小阿巴:这还真难不倒我,这几年我苦练增删改查,分页写得很溜的!纸笔呈上来,看我给你手写 SQL:

复制代码
select * from message
where user = '鱼皮'
limit 0, 20;

我说:Emm,老弟啊,听我一句劝,咱先别想着做微信了,先实现一个消息管理系统吧。

小阿巴:怎么说?吾 SQL 不亦精乎?

其实这也是一道经典的场景题:即时通讯项目中怎么实现历史消息的下拉分页加载?

下面鱼皮给大家讲解一下。

如何实现下拉分页加载?

业务场景

一般在即时通讯项目(比如聊天室)中,我们会采用下拉分页的方式让用户加载历史消息记录。

区别于标准分页每次只展示当前页面的数据,下拉分页加载是 增量加载 的模式,每次下拉时会请求加载一小部分新数据,并放到已加载的数据列表中,从而形成无限滚动的效果,确保用户体验流畅。

比如用户有 10 条消息记录,以 5 条为单位进行分页,刚进入房间时只会加载最新的 5 条消息:

下拉后,会加载历史的第 6 - 10 条消息:

理解了业务场景后,再看下实现方案,为什么不建议使用传统分页实现下拉加载。

传统分页的问题

在传统分页中,数据通常是 基于页码或偏移量 进行加载的。如果数据在分页过程发生了变化,比如插入新数据、删除老数据,用户看到的分页数据可能会出现不一致,导致用户错过或重复某些数据。

举个例子,对于即时通讯项目,用户可能会持续收到新的消息。如果按照传统分页基于偏移量加载,第一页已经加载了第 1 - 5 行的数据,本来要查询的第二页数据是第 6 - 10 行(对应的 SQL 语句为 limit 5, 5),数据库记录如下:

结果在查询第二页前,突然用户又收到了 5 条新消息,数据库记录就变成了下面这样。原本的第一页,变成了当前的第二页!

这样就导致查询出的第二页数据,正好是之前已经查询出的第一页的数据,造成了消息重复加载。所以不建议采用这种方法。

推荐方案 - 游标分页

为了解决这种问题,可以使用游标分页。使用一个游标来跟踪分页位置,而不是基于页码,每次请求从上一次请求的游标开始加载数据。

一般我们会选择数据记录的唯一标识符(主键)、时间戳、或者具有排序能力的字段作为游标。比如即时通讯系统中的每个消息,通常都有一个唯一自增的 id,就可以作为游标。每次查询完当前页面的数据后,可以将最后一条消息记录的 id 作为游标值传递给前端(客户端)。

当要加载下一页时,前端携带游标值发起查询,后端操作数据库从 id 小于当前游标值的数据开始查询,这样查询结果就不会受到新增数据的影响。

对应的 SQL 语句为:

复制代码
SELECT * FROM messages
WHERE id < :cursorId
ORDER BY id DESC
LIMIT 5;

扩展知识

其实游标分页是一种经典方案,它的应用场景很多,特别适用于增量数据加载、大数据量的高性能查询和处理。除了 IM 系统获取历史消息记录之外,常见场景还有社交媒体信息流、内容推荐系统、数据迁移备份等等。

游标分页还有很多扩展知识,篇幅原因就不在这里展开了,感兴趣的同学可以在我们的 程序员面试刷题工具 - 面试鸭 上阅读。

最后

小阿巴听完,长叹道:唉,没想到光是这么一个小功能,就把我难住了。

我说:你可别这么想。。。难住你的,可不止这一个小功能啊!想做一个成熟的 IM 系统,除了最基础的消息发送和获取功能外,你得去学习 WebSocket 实时通讯、得考虑到消息收发的性能、得考虑到消息的顺序和一致性、得考虑到消息的存储成本和安全,等等等等。可没那么容易。

小阿巴:得,那我先去做消息管理系统了!🐶

更多

💻 编程学习交流:编程导航:https://www.code-nav.cn

📃 简历快速制作:老鱼简历:https://laoyujianli.com

✏️ 面试刷题神器:面试鸭:https://mianshiya.com

相关推荐
STER labo20 分钟前
mysql配置环境变量——(‘mysql‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件解决办法)
数据库·mysql·adb
dreamZhanglx40 分钟前
MySQL进阶
数据库·mysql
xmjd msup41 分钟前
MySQL 函数
数据库·mysql
jefl jxak2 小时前
mysql用户名怎么看
数据库·mysql
unDl IONA2 小时前
mysql之如何获知版本
数据库·mysql
俺不要写代码2 小时前
数据库:约束
数据库·mysql
陈随易2 小时前
bun将会支持Bun.image,你怎么看?
前端·后端·程序员
vbzcro_5152 小时前
Java GC 日志读取与分析
编程
WL_Aurora3 小时前
MySQL 5 卸载到 MySQL 8 安装完整指南(不踩坑版)
数据库·mysql
灰阳阳3 小时前
MySQL的基本架构
数据库·mysql·架构