问题
我有两个对象列表。每个对象包含以下内容:
GUID
(允许确定对象是否相同 - 来自业务
观点)Timestamp
(每次更新当前UTC
对象改变了)Version
(正整数;每次递增
对象改变了)Deleted
(布尔标志;改为切换为“true”
实际对象删除)Data
(一些有用的有效载荷)接下来,我需要根据这些规则同步两个列表:
GUID
的对象仅在一个列表中显示,则应将其复制到另一个列表GUID
的对象,那么Version
较少的实例应该替换为Version
更大的实例(如果版本相同则无关)现实世界的要求:
解决方案#1(目前已实施)
T
)Timestamp
>的所有对象。 T
(即最近修改过的;在制作过程中......>(T
- 天)以获得更好的稳健性)Version
被比较并保存到专有列表(如果需要)PROCS:
缺点:
T
,这会使算法变得脆弱:很容易同步最后的更新,但很难确保列表完全同步(使用像{1970}这样的最小T
-01-01只挂起同步过程)我的问题:
P.S。已经查看过,不重复:
答案 0 :(得分:3)
所有答案都有一些值得的。总而言之,这是我正在寻找的编译答案,基于最终实现的工作同步系统:
一般来说,使用Merkle trees 。它们在比较大量数据方面非常有效。
如果可以,每次需要时都从头开始重建哈希树。 检查重建哈希树所需的时间。最有可能的是它非常快(例如,在我的情况下,Nexus 4重建树的20k项目需要~2秒:从DB获取数据需要1.8秒+构建树需要0.2秒;服务器执行速度提高约20倍),因此,您不需要将树存储在数据库中并在数据更改时进行维护(我的第一次尝试只是重建相关的分支 - 它实现起来并不复杂,但非常脆弱)。
然而,如果没有进行任何数据修改,缓存和重用树就可以了。修改发生后,使整个缓存无效。
0000
→(带有GUID 0000*
的项目的哈希值)0001
→(带有GUID 0001*
的项目的哈希值)ffff
→(带有GUID ffff*
的项目的哈希); 000
→(哈希哈希000_
)00
→(哈希哈希00_
)_
)因此,树有65536个叶子,需要2 Mb的内存;每片叶子覆盖~N / 65536个数据项。二进制树在内存方面的效率提高了2倍,但实现起来却更难。
我必须实现这些方法:
getHash()
- 返回根哈希值;用于初步检查(如上所述,
96%,这是我们需要测试的全部内容; getHashChildren(x)
- 返回哈希值x_
列表(最多16个);用于有效的单一请求发现数据差异; findByIdPrefix(x)
- 返回GUID为x*
的项目,x必须包含4个字符;用于请求叶子项目; count(x)
- 返回GUID为x*
的商品数量;当合理地小时,我们可以逐个检查树检查并转移一堆项目; 就每个分支传输少量数据进行同步而言,它非常敏感(您可以随时检查进度)+对于意外终止非常可靠(例如,由于网络如果需要,可以从最后一点轻松重启。
version_1
= version_2
,但是hash_1
!= hash_2
}:在这种情况下,你必须做出一些决定(可能是用户的帮助或比较时间戳作为最后的手段)并用另一个重写某个项目来解决冲突,否则你&# 39; ll最终得到未同步和不可同步的哈希树。(GUID, Version)
对。答案 1 :(得分:1)
我想到了两个建议,第一个建议可能是你已经做过的事情:
1)不要发送带有时间戳的整个项目列表>相反,发送带有时间戳>的对象的(UUID,Version)元组列表。然后另一方可以找出需要更新的对象。发回那些UUID以请求实际对象。这样可以避免在有时间戳的情况下发送完整对象。 T,但在另一方面已经更新(或已经有最新版本)。
2)不要立即处理完整列表,而是以块为单位,即首先同步10%,然后是下一个10%等,以避免为大同步一次传输过多数据(并允许如果连接断开,则重新启动点)。这可以通过例如从所有UUID开始,校验和相当于1模10,然后1模10等。
另一种可能性是主动同步,例如异步发布机会,可能通过UCP(不可靠而不是TCP)。当您需要当前信息时,您仍然需要同步,但大多数情况都是最新的。
答案 2 :(得分:1)
您需要在上次同步时不存储上次同步的时间,而是存储对象的状态(例如,对象数据的哈希)。然后,将每个列表与存储的列表进行比较,找到每侧更改的对象。
这比依靠时间更可靠,因为时间要求双方都有同步计时器,这给出了精确的时间(在大多数系统中并非如此)。出于同样的原因,您基于时间+版本检测更改的想法可能比最初看起来更容易出错。
此外,您最初不会传输对象数据,只会传输GUID。
顺便说一下,我们已经制作了一个框架(免费提供源代码),可以解决您的问题。我没有给出这个链接,因为一些有才华的人会抱怨。