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

40,542 阅读6分钟

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

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

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

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

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

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

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

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

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

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

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

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

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

select * from message
where user = '鱼皮'
limit 020;

我说: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 实时通讯、得考虑到消息收发的性能、得考虑到消息的顺序和一致性、得考虑到消息的存储成本和安全,等等等等。可没那么容易。

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

更多

💻 编程学习交流:编程导航
📃 简历快速制作:老鱼简历
✏️ 面试刷题神器:面试鸭