lmk123 / blog

个人技术博客,博文写在 Issues 里。
https://github.com/lmk123/blog/issues
623 stars 35 forks source link

离线数据同步的具体实现思路(二) #84

Open lmk123 opened 3 years ago

lmk123 commented 3 years ago

《离线数据同步的具体实现思路》一文中,我实现了第一个版本的离线数据同步功能,最近在实际项目中应用了这个实现,又想到了一些改进的点。

上传本地数据库变更与下载服务器数据库变更应该分开

在之前的实现当中,同步数据的方法有且只有 sync,这个方法先是提交了本地数据库的变更,然后又下载了服务器数据库的变更并保存到本地数据库。但在实际场景中,上传与下载很有可能是分开的。

这一点是受到了 iPhone 的“备忘录”的启发。当我在网页版的备忘录 https://www.icloud.com/notes/ 里编辑我的备忘录之后,然后在手机里打开备忘录时,我首先看到的是我在网页版编辑之前的内容,然后备忘录会开始检查更新,大约 1 秒后内容就变成了我在网页版编辑之后的内容。

所以,我们应该把 sync 一分为二成为两个方法 commitpull

pull 方法也会下载到由自己提交的本地数据库变更,所以在把服务器数据库变更保存到本地时,还需要检查一下这些变更里面有哪些数据已经存在于本地数据库了。

变更记录的删除时机

上一篇文章里同样也提到,随着时间的推移,用于保存变更记录的数据库表会越来越大。不过,我想到了一个删除变更记录的方法。

每当执行完 pull 之后,如果客户端成功将服务器数据库变更保存下来了,就调用服务器的一个接口,将这次数据更新的时间传过去。

服务器的这个接口会将每个端的数据更新时间保存在 session 里,然后找到这个用户的所有 session 当中,最先检查更新的时间点,而我们只需要删除这个时间点之前的变更记录就可以了。

举个例子:

假设用户一开始仅仅只登录了网页端,那么每次用户 pull 数据之后,按照这个做法,服务器会清空变更记录,但这是安全的,毕竟这些记录不会有其它端获取。

如果随后用户又登录了手机端,手机端首先会完整下载一次数据,我们需要把这个时间点作为更新时间保存到 session,然后按照这个做法,变更记录不会有改变——因为这个时间点肯定大于网页端最后一次 pull 数据的时间点,而在网页端最后一次 pull 数据时,在这之前的变更记录已经被删除了。

此时,手机端和网页端的数据可能是一致的,也有可能不是,比如用户在网页端最后一次 pull 了数据之后新增了一些待办事项,但由于断网了,这些数据没有提交到服务器——但这无关紧要,等到网页端联网了并提交这些变更之后,手机端还是可以在用户查看待办事项时将这些变更 pull 下来。

如果用户接下来做了这些事情:

  1. 前天上午 10 点,用户在网页端新增(commit)了一些待办事项
  2. 昨天晚上 8 点,用户在手机端查看(pull)了待办事项 A 并新增(commit)了一些待办事项
  3. 今天下午 1 点,用户在网页端查看(pull)了待办事项

那么,当今天下午 1 点用户 pull 了数据之后,我们就把这个时间点发送给服务器;服务器在跟手机端的更新时间点(昨天晚上 8 点)进行对比后,发现手机端的更新时间要早于网页端,那么服务器就可以安全的删除昨天晚上 8 点之前的变更记录了。

当手机端下次 pull 待办事项的时候,会向服务器获取“昨天晚上 8 点”之后的变更记录,而这些变更记录还是在的,因为我们删除的是“昨天晚上 8 点”之前的变更记录。

总结

离线数据同步的思路到目前为止算是比较完善了,但目前只讨论了新增和删除数据的情况,对于修改数据的情况(例如修改待办事项的标题)还没有讲到,等以后实际遇到了更新数据的情况,我会再将想法记录下来。