在单台机器上实现TB级的数据对比

在单台机器上实现TB级的数据对比

前几天我在做公司项目的时候,需要对数据进行比较。数据量不大,也就几千条而已。当时功能完成后,我开始思考一个问题。就是当对比的文件是特别大的时候,我还能实现吗?那现在对问题做出假设:

问题:

现在有两个超大文件集,原始数据集和目标数据集,每个文件集中都有超过1亿的记录,数据中存在重复的记录,编写一个程序,可以找出两个数据集之间的差异,求解出三类数据:

原始数据集中存在,目标数据集中不存在,为待删除的数据记录。

原始数据集中存在,目标数据集中也存在,但内容不一致,为待修改的记录。

原始数据集不存在,目标数据集中存在,为待添加的记录。

还有一个限制条件,内存限制8G。

求解思路:

普通求解

先说我在小数据量的情况下,我使用的方法吧,

  1. 先将原始数加载到内存中,使用一个map记录,以数据主键为key,以数据为值.当数据存在时,后面的数据覆盖前面的数据。

  2. 目标数据集也装载到内存中,同样使用一个map记录.

  3. 遍历原始数据,以原始数据的key到目标数据的map中去获取,如果获取不到,说明当前数据仅在原始数据中存在,在目标数据中不存在,添加到待删除的记录中,如果可以获取到,检查数据与目标数据是否一致,如果一致,说明原始数据与目标数据完成一样,就跳过当前数据,如果数据不一致,说明目标数据做出来修改,则记录到待修改记录的原始数据中。

  4. 遍历目标数据,以目标数据的key到原始数据的map中去获取,如果获取不到,说明当前数据仅在目标数据中存在,在原始数据中不存在,添加到待添加的记录中,如果可以获取到,需要再检查数据与原始数据是否一致,如果一致,说明原始数据与目标数据完全一样,跳过当前数据;如果数据不一致,说明目标数据做出了修改,则记录到待修改记录的目标数据中。

经过这4个步骤后,数据之间的差异就被对比出来了。

错误方案:布隆过滤器

那切回到大数据集的对比吧,用这种方案肯定是无法实现的。那要怎么解呢?

还是参照普通求解的一个方案。只不过采用了布隆过滤器来替代map.但没有想到却是一条歪路。

由于布隆过滤器与map很相似,且空间占用更优,我就优先想到这个方案,具体是这样子的:

  1. 构建两个布隆过滤器组。原始布隆过滤器组和目标过滤器组,每个布隆器组中存在多个布隆过滤器,通过hash函数将数据均匀分布到这一组的布隆过滤器中,每个布隆过滤器控制空间占用不超过50%。

  2. 遍历原始数据,将原始数据的key和数据,分别填充到原始布隆过滤器组中。

  3. 遍历目标数据,将目标数据的key和数据,分别填充到目标布隆过滤器组中。

  4. 遍历原始数据,检查原始数据的key是否在目标布隆过滤器组中,如果不存在,说明数据在原始数据中存在,在目标数据中不存在,则记录到待删除的文件中;如果原始数据的key存在于目标布隆过滤器组中,检查原始数据的是否存在于目标布隆过滤器组中,如果存在,说明原始数据与目标数据一致,跳过当前数据,如果原始数据与目标数据不一致,说明数据不存在,记录到待修改前的文件中。

  5. 遍历目标数据,检查目标数据的key是否在原始布隆过滤器组中,如果不存在,说明数据在原始数据中不存在,在目标数据中存在,则记录到待添加的文件中;如果目标数据的key存在于目标布隆过滤器组中,检查目标数据是否存在于原始布隆过滤器组中,如果存在,说明原始数据与目标数据一致,跳过当前的数据,如果原始数据与目标数据不一致,说明数据不存在,记录到修改后的文件中。

  6. 经过上面的步骤后,各种数据就被筛选出来,包括添加的数据文件,修改前的数据文件,修改后数据文件,删除的数据文件,接下来就是将修改前与修改后的文件合并了。由于这里都是待修改的数据,两边数据key都会存在,但这里存在一个问题,那就是修改前的数据文件与修改后的数据文件并非有序的,无法直接输出。那接下来就是先修改前与修改后的文件有序化的过程了。

  7. 这里我使用的是归并排序方案。先将修改之前的文件切分,按32M为大小为限制,并且保证数据都是完整的行,切分出一个个文件,并不保证每个文件都是32M大小,只是最接近于32M的大小.

  8. 待修改前文件切分完成后,对每个小文件进行排序操作。使用原生java的插入排序方式,虽然时时间复杂度为O(n的平方)但排序的稳定,不会改变相同数据的前后顺序。至到所有小文件排序完成。

  9. 修改前的的所有小文件排序完成后,开始对修改后的文件进行切分了,同样使用原始文件切分的方式,以32M为大小限制,并我看也是数据都是完整的行。切分完一个个文件。

  10. 修改后的文件切会完成后,开始对切分文件排序,同样使用插入排序。至到所有小文件排序完成。

  11. 终于来到最后一个步骤了,那就是对修改前与修改后的文件进行合并了。按修改前–>修改后这样的对应方式。将数据标识出来。

看似一个完美的方案。当我在测试1亿的数据后,发现一个致命问题。那就是无法保证修改前与修改后的数据都会存在。最后通过对数据检查。发现布隆过滤器中还是会存在误判的问题。看来还是没有对布隆过滤器深入到位,天真的以为只要空间占用低点,就不会出现误判。现实就这么打脸。还是按在地上摩擦的那种。

正确方案:归并排序对比

不过这个错误的做法也不是一无是处。却为我开劈了一个思路。那就是既然归并排序能将两边数据都排序,那排过序的文件,能否对比出差异呢?带着这个问题,我开始思考这个问题能不能解呢?即问题变成了,对两个排序相同的数据,找出差异?还是分情况还做下分析吧:

  1. 原始数据1的主键小于目标数据1的主键。这种情况说明数据在原始数据中存在,在目标数据中不存在,即为待删除的数据,加入待删除的文件中。

  2. 原始数据1的主键等于目标数据1的主键。这种情况说明数据在原始数据中存在,在目标数据中也存在,可能为待修改的数据,也可能前后数据都没有修改,什么操作都不用。那接下来就是检查数据内容是否一致了,通过对比,数据可能一致,则直接跳过当前数据,数据可能不一致。将数据加入到待修改的文件中。

  3. 原始数据1的主键大于目标数据1的主键。这种情况说明数据在原始数据中不存在,在目标数据中存在,即为待添加的数据,加入待添加的文件中。

通过上面这三情况一分析。排序后的数据是可以直接对比出差异的。好像不对,我是不是漏了什么东西?原始数据1和原始数据1最小的的在个文件呢?一定在第一切分的文件中吗?由于数据是按大小切分的,排序之后的最小在哪个文件是不确定的。那要先找到第一个最小的那个数据?这如何找到呢?可以这样分析这个问题,针对所有已经 排序的小文件来说,最小的数据在哪呢?这个肯定在某个文件的第一行。只要读取所有文件的第一行数据,然后遍历对比,这样就可以找到最小的数据了,其实这就是归并排序时的合并逻辑。

在这里插入图片描述

但还存在一个问题,那数据的key可能会是重复的。如果我们对重复的数据加以规定,当数据的主键的key相同时,取最后操作的记录。那上面的对比流程还能正常工作吗?或者如何改进呢?由于原始数据与目标数据中的重复问题是一样的,只讨论原始数据中带有重复数据的问题如何解决了!

重复存在着两种情况:

  1. 单文件内的有序数据重复。
  2. 多文件间的数据重复。

单文件内的有序数据重复问题:

由于单文件内数据重复仅针对主键相同的数据,即当key相同时,顺序遍历查找最大值即可。

多文件间的数据重复问题:

这个问题就需要优先完成单文件内有序重复数据查找最大值。将最大值记录在读取数组中。再遍历这个数组,当主键key相同时,再查找最大值。

还是拿一个具体一示例来说明吧。

在这里插入图片描述

来解释下吧,从文件中读取第一行的数据“5, 481086186500,1,2”,保存到临时变量中,再从文件中读取一行数据“5, 481086186510,1,2”与临时变量中的数据做对比,key一致,则保留时间最新的数据"5, 481086186510,1,2"。将最新的数据到临时变量中。

再就来多组数据的合并吧

在这里插入图片描述

我来解释下吧,首先分别从文件1和文件2读取首行数据至临时最小变量中,就是图中的“5, 481086186500,1,2”和“5, 481086186520,1,2”,再对比临时变量key相同的数据,将时间最近的数据保留。输出完毕后,再分别从文件和和文件2读取数据填充至最小临时变量中。一直轮训这个过程就可以将数据输出了。

来整体看下这个数据对比的流程:

在这里插入图片描述

总结:

​ 当我将整个数据对比完成后,才发现原来这就是一个稍加改造的归并排序。在归并排序的合并数据时将逻辑稍加改造 ,按数据的差异性的结果就比出来了。特别需要注意重复数据合并。按照数据的一个差异性将数据输出到添加数据的文件、修改数据文件、删除数据文件。这样一个超大型的文件对比就完成了。

如果想直接查看代码,欢迎来我的github查看,
程序的入口:
https://github.com/kkzfl22/datastruct/blob/master/src/main/java/com/liujun/datastruct/datacompare/bigfilecompare/BigFileCompareMain.java

由于整个过程中涉及代码众多,接下来将分为多章节来讲解对比的实现。敬请期待。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值