超大型文件比较,内存不足,只能分页读区再匹配,但头都秃了,也没想到优化的方式,朋友们帮帮忙啊。

350 天前
 FeifeiJin
有两个本地文件,PersonListA.csv 和 PersonListB.csv ,文件过于巨大,我需要分批读取到内存里,变成 List<Person> A 和 List<Person> B ,我现在要把 A 和 B 进行对比,获得下面结果:

Person 有两个属性,一个 Name ,一个 Age ,Name 是唯一的,但两个列表中相同 Name 的人 Age 不一样

1 ,找出存在于 A 但不存在于 B 的元素。

2 ,找出存在于 B 但不存在于 A 的元素。

3 ,找出两个列表中不同的元素。

有以下限定条件:

1 ,本地文件过大,无法一次性读取到内存中,需要分页进行比较。

2 ,无法对两个列表进行排序,数据在列表中都是乱序的,需要用 Name 进行匹配。

如果要实现以上 function ,需要用到什么算法,请分析后给出算法的名称。

现在的策略是,把 A 中的数据分为 10 组 10 条,然后把 B 中的数据分为 10 组 10 条的数据,分别拿 A 中的每一组数据和 B 中的每一组数据进行比较,这个方法叫做 GetUniqueResult

1 ,在找到只存在 A 不存在 B 的元素调用一次 GetUniqueResult 。

2 ,在找到只存在 B 不存在 A 的元素调用一次 GetUniqueResult 。

3 ,在通过 FindDiffer 找到 A 和 B 中不同的元素。

相当于我需要循环 3*(10^10)次才能结束,其中也有使用 HashMap 来进行优化,使得实际执行次数小于 3*(10^10)次。(比如 A 中的第一组数据在和 B 的所有数据比较时,如果第一组的所有数据都找到了,就提前结束。)

我现在想问的是,有没有什么更好的算法能从 3*(10^10) 优化到 (10^10),如果能跟优化到 N ( 1 )

有没有什么方法能只对调用一次 GetUniqueResult ,获得全部结果。

-----------------------------------------------

浓缩版:

我有两个本地 FileA.csv 和 FileB.csv ,分别存储了从 Oracle 中和 PostgreSQL 导出的同一个表。

1 ,单个文件超过 10GB ,无法一次性读取到内存中。

2 ,两个导出的文件是乱序的,需要用主键在程序里进行数据匹配(文件无法修改)。

需要获取结果:

1 ,在 A 中存在,但 B 中不存在的数据。

2 ,在 B 中存在,但 A 中不存在的数据。

3 ,在 A 和 B 中都存在,但有差异的数据。


可以有什么办法吗?或者给我一些算法关键字,比如我现在使用的是类哈希分页对比。
6463 次点击
所在节点    程序员
74 条回复
FlytoSirius
350 天前
我的第一感觉是, 这个事情应该导入的数据库表里再进行相关操作,
写程序对比不是最佳方案.

10GB 单表在数据库里也不算多大.

你要的那三个比较结果在 SQL 里都是很容易得到的.
hefish
350 天前
我觉着 ls 的大佬说的对,放 mysql 里就完事了。
FeifeiJin
350 天前
@FlytoSirius
@hefish
宝,很感激你们的回复。

因为现在的目的就是用外部程序去验证两个数据库中的数据是否完全一致,且有种种条条框框加成下,只能用程序去读 CSV 去验证哈。
min
350 天前
买内存条啊
FlytoSirius
350 天前
@FeifeiJin
那能不能程序直接连接 两端数据库进行对比呢? 毕竟导出这样的两个数 GB 的大表是个容易出问题的过程.

但, 如果就得用程序去对比文件, 那确实要花点时间研究下具体实现.
FeifeiJin
350 天前
@FlytoSirius 真的不能。
FeifeiJin
350 天前
@min 谢谢您的回复。
Nosub
350 天前
如果是 Java 可以试试阿里的 easyexcel ,https://github.com/alibaba/easyexcel
me1onsoda
350 天前
允许误差的情况下,布隆过滤器可以满足 12
ck65
350 天前
前阵子有个 1 billion row 挑战,OP 可以参考一下看有没有帮助

- https://1brc.dev
- https://mrkaran.dev/posts/1brc/
leonshaw
350 天前
那就用 sqlite
FeifeiJin
350 天前
@Nosub
@me1onsoda
谢谢,但是这边用的是 C#。

布隆过滤器我正在看,大概率是不允许误差。
FeifeiJin
350 天前
@ck65
我觉得有帮助,我看一看。

就算不能解决我的问题,也能从里面看到很多思路。
FeifeiJin
350 天前
@leonshaw 宝,谢谢。但不能。
ipwx
350 天前
大概有多少条数据。

数据条数不多(千万级别)但是每一行的列多的话,可以考虑先扫一遍两个 csv ,记录一下每一个 csv 里面 name 和 file byte offset 的对应关系。然后在内存里面根据 name 进行你的几个操作,最后根据记录下来的 offset 从 csv 里面重新读出来对应的 Person 。
leonshaw
350 天前
@FeifeiJin 为什么不能?
geelaw
350 天前
第一个问题就是你是否有足够的磁盘空间,如果有的话,完全可以先排完序再说。

假设你使用 64 位操作系统,先分别排序两个 csv ,这样做:

1. 把 x.csv 映射到虚拟内存。
2. 扫描一次,计算行数 n 。
3. 建立一个长度是 8n 字节的文件 x.dat ,映射到内存,把它看成长度是 n 的 uint64 数组 index 。
4. 扫描 x.csv ,在 index[i] 放置第 (i-1) 行开始的位移。
5. 对 index 的元素 z 按 x.csv 从 z 处提取出的字符串升序排序。
6. 保存 x-sorted.csv 。

上述操作需要 O(n log n) 的时间。

然后同时把 a.csv, a.dat, b.csv, b.dat 映射到虚拟内存,并用有序合并算法计算需要的三个结果,这需要 O(n) 的时间。

额外的磁盘空间复杂度是 O(n),具体来说,显然不会超过 20 GB 。
Nosub
350 天前
@FeifeiJin 即便如此,我觉得 c#程序员去用这个库,也没有问题,建议你试试看,写个命令行程序就可以了,不试试怎么知道行不行,这个库肯定解决了你的读取问题了。
lrjia
350 天前
可以把 name 分组,比如先把两个文件中所有 a 开头的行读入内存比较,然后再比较 b 、c 。分组粒度大小按照内存大小来。
FeifeiJin
350 天前
@geelaw
谢谢您, 这个方法是行得通的。目前不想外部排序,实在走不通,再走这条路。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1015733

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX