因为markdown的样式太丑了,懒得再调整了,我另外再贴一个github的博客该文的 github连接git
最近在工做中遇到一个比较棘手的问题,客户端从服务端同步数据的问题。
背景简介:客户端有N个,客户端上的同步时间,各不相同。同步的时候,是一次获取10条数据,多批次获取。即分页获取。
在代码中存在两种同步的方式:github
惟一约束
的ID
来实现同步。只适用于数据量小的表,浪费网络流量。大于
客户端最新时间
的数据;依赖于时间戳
,问题时间戳不惟一
存在相同时间点下面多条数据,会出现数据遗漏,也会重复拉取数据,浪费网络流量。本文的所使用到的解决办法,就是结合了惟一ID和时间戳,两个入参来作增量同步。本文也只作逻辑层面的说明。算法
表结构:ID 具备惟一约束, Name 姓名, UpdateTime 更新时间;如今问题的关键是ID为3,4,这两条时间点相同的数据。
假如一次只能同步一条数据,如何同步完ID 2后,再同步 ID 3。sql
ID | Name | UpdateTime |
---|---|---|
1 | 张三 | 2018-11-10 |
2 | 李四 | 2018-12-10 |
3 | 王五 | 2018-12-10 |
4 | 赵六 | 2018-11-20 |
5 | 金七 | 2018-11-30 |
经过 UpdateTime 和 ID 这两种数据,经过某种运算,生成新的数。而这个新的数
具有可排序和惟一;同时还要携带有ID
和UpdateTime
的信息。
简单表述就是,具备一个函数f: f(可排序A,可排序惟一B) = 可排序惟一C 。 C 的惟一解是 A和B。RSA加密算法数据库
我想出了一个方法,也是生活中比较经常使用的方法:服务器
权重
必须大于ID
的可能最大值。如: 20181210 * 100 = 2018121000,Max(ID)<999ID
。如: 2018121000 + 3 = 2018121003。这个时候,2018121003 这个数,既包含了UpdateTime
和ID
的信息,又具备可排序和惟一性。用它做为增量更新的判断点,是再好不过的了。
可是它具备很大的缺点:数字太大了,时间转化成数字,目前仍是用的是天级别,若是换成毫秒级别呢。还有ID可能的最大值也够大了,若是是int64那就更没得搞了。markdown
这个方法理论上可行,实际中不可用基本不可行,除非找到一种很是好的函数f;
PS: 个人直觉告诉我: 很可能存在这种函数,既知足个人须要,又能够克服数字很大这个问题。只是我目前不知道。网络
修改数据内容,使 UpdateTime
数据值惟一。缺点也比较明显:框架
还有一种办法,就是在数据库中,增长一个新的字段,专门用来同步数据的时候使用。
比方说,增长字段 SyncData
int 类型。若是 UpdateTime 发生了改变,就把它更新为 SyncData = Max(SyncData) + 1
;
也就是说, SyncData
这个字段的最大值必定是最新的数据,SyncData
的降序就是 更新时间的降序。SyncData
是更新时间顺序
的充分没必要要条件。函数
总的来讲,这种办法是比较好的,但缺点也比较明显:
首先,先来分析一下,一次提取10条数据,提取的数据,存在的可能状况。再次说明前提,先时间倒序,再ID倒序。Order By UpdateTime DESC, ID DESC
可能状况以下图,能够简化为三种:
其中情景1
和情景3
,能够把查询条件变为:WHERE UpdateTime > sync_time LIMIT 10
可是情景2
的状况不能使用大于>
这个条件。假如使用了大于>
这个条件,情景2
就会变成情景1
或情景3
或图3
这种状况。不是包含部分了,须要额外特别处理。
注:图3的结束点 ]
不重要,下面情景5有解释。
提取的起始点:也就是说图中[
左中括号的位置,须要准肯定位这个位置。
至于结束点:图中]
右中括号的位置是在哪里。这个就不重要了,由于下一次的分页提取的起始点
,就是上一次的结束点。只须要关注起始点就足够了。
而根据起始点,又能够把情景2
,再作一次简化:
针对情景4
。这个时候,时间戳sync_time
一个入参就不够了,还额外须要惟一键ID来准肯定位。能够把查询写做:WHERE UpdateTime = sync_time AND ID > sync_id LIMIT 10
。
若是查询的行数 等于 10,则是图4;小于 10,则是图2,图6,图7的状况。
针对情景5
。依旧可使用:WHERE UpdateTime > sync_time LIMIT 10
完整的分页过程的步骤:
1、先用起始点来过滤:WHERE UpdateTime = sync_time AND ID > sync_id LIMIT 10
,查询结果行数N。若是 N=10
是图4的状况,则结束,而且直接返回结果。若是 0<= N <10
,则进行第二步,其中N=0
是图1,图3,图5,图···的状况;
2、再用时间戳查询:WHERE UpdateTime > sync_time LIMIT 10-N
,查询结果行数 M ,0<= M <=10-N
;这个阶段,是否同一个时间点都不重要了。只须要按着顺序
取已排序的数据就能够了;
3、把一和二的结果集合并,一并返回。
4、重复步骤一二三,直到,分页获取的最后一条数据的ID
,是服务端数据库中最新的ID;(防止存在,刚好这十条是所须要获取的最后十条)。
服务端中最新ID获取:Select Id From myTable Order by UpdateTime desc,ID desc Limit 1
;
寻找关键信息,以及具备指标意义的数据,或者数据的组合。
拆分问题,简化问题
同步数据的可能性
,慢慢归类,简化后;才发现。问题没有那么难,仅仅是起始点这一个小小的问题。使用逻辑分析和哲学概括
惟一
和可排序
;跳出了具体字段,使用场景的框架束缚,而去考虑这两种性质怎么结合的问题;查询的方法
;有点绕,打个比方,既要优化最终产品,也要去优化制做工艺;最后,我认为我最近的逻辑分析能力,好像有比较大的提高。