最近在做一个聊天工具的需求,遇到一个这样的场景:
所有的聊天记录都是存储在 APP 本地的,而本地使用的是数据库是以 key-value 形式保存的。
刚开始的设想很简单,我把「聊天 id」当做 key , 把「聊天记录」当做 value 存起来,在用户进入聊天页面时,我直接查询出当前会话的所有记录就行了。
这当然符合逻辑,并且也能正常工作,但是当聊天记录比较庞大的时候就会出现问题:
太过早远的历史记录被阅读的几率很小,并不需要一并读取出来。
一般用户需要阅读的记录就是最新的那么几十条,可是现在一进入聊天页面就把所有消息一股脑的读取出来了,这样有点浪费资源,于是我想到了分页加载。
刚开始的时候没啥思路,去 Google 了解了一下普通数据库是实现分页加载的 … 但是查到的都是一些 SQL 语句,教你如何分页查询数据 …
但是由于本地使用的只是简单的 key-value 结构存储,并不是完整的数据库,所以并不能简单的通过 sql 查询来实现分页加载的效果 …
于是我开始刷起了微信,打算看看它是怎么做的,刷着刷着还真发现一点东西:
当你滑动查看聊天记录的时候,如果开始分页加载了,记录上面就会带上时间(这个逻辑是我瞎猜的,不过给了我灵感)
时间?!我是不是可以在「聊天 id」后面加上「时间」来组成一个 key 呢?
大概的思路如下:
1. 首先将 key 改成「聊天 id+ 时间」的形式,这样聊天记录就可以按照时间进行划分
2. 时间的划分是有规律的,比如我们设定存储的间隔是三分钟,这样我们就可以通过当前 key,往前推三分钟来得到上一个 key
举个例子,当前的 key 是「聊天 id2020-0607-1059」那么上个 key 就是「聊天 id2020-0607-1056」。
他们间隔为 3 分钟。
听上去似乎挺靠谱的,但实际做起来还是有问题:
1. 用户并不会一直和人聊天,所以往前推时间来得到的 key 中并不一定有值,可能需要往前推好几次,也就是查询好几次才能得到聊天记录。
2. 如果只是单纯的往前推时间,我们并不知道什么时候数据已经被查完了。
虽然有问题,但是我们用「聊天 id+ 参数」的形式来做分页说明是可行的。
那既然参数用时间不行,我们就换成别的,例如我们构造一个自增的 id。
你可以自定义聊天记录存储的时机,只是每次存储的时候都是使用「聊天 id+ 自增 id」作为 key 来存储。
举个例子:
1. 初始 id 为 1,我们每 30 条聊天记录存储一次
2. 存储的 自增 id 为上一个 自增 id 加 1,然后和 聊天 id 组成 key
我们再看上面的两个问题,会发现都已经被解决了。
1. 因为只要存了,那肯定是有记录
2. 初始 id 是 1,所以我们只要查到 1,就表示所有数据都被查完了
这样看起来似乎完美的解决了问题,但是 …
设想一下这样的场景:
用户有两台手机,两台手机上都存储着聊天记录,这个时候需要将两台手机上的聊天记录合并在一起,如果你的 key 只是用 自增 id 构造的话,那么聊天记录的时间顺序处理就会变得很麻烦
所以,为了解决这个场景,我们的 key 还是得用时间去构造,这样就可以方便我们做聊天记录合并时的排序。
但是上面的问题该如何解决呢?
我的解决方式是引入一张新的表,表的 key 是「聊天 id」,而值就是「聊天 id+ 时间」也就是查询聊天记录表时需要的 key,那我们的整个加载流程就变成了这样:
1. 进入聊天页面后,我们先通过「聊天 id」查出 聊天记录表 所对应的 key 列表
2. 使用 key 列表 去分页查询 聊天记录
这样的话,上面说的两个问题也被解决了,并且因为我们的 key 上带了时间,可以提高我们在消息合并时的效率。
结尾
以上就是我对本地 key-value 数据库如何实现聊天记录分页加载的思考和分享,其实知道了这个思路之后,实现起来是没有什么难点的。
如果大家有任何问题,都可以在评论区留言,欢迎讨论,一起成长。
谢谢~