我的问题是,我有两个数据库表,一个有大约 10 000 条记录,另一个有 5 000 000 条记录,每条记录有 56 列。现在我要做的是,将这 10 000 条记录中的每条记录与另一张表中的每条记录进行比较,然后找到 10 条最佳记录(比较列值等)。所以我在寻找一些想法如何在合理的时间内做到这一点,因为到目前为止这将花费我太长时间......我一直在寻找互联网并找到了例如 hadoop,但我从未使用过它而且我不确定它是否能在我的情况下完成这项工作......我的机器有 2 个内核和 4GB 内存,所以它不是公牛。感谢您提供任何答案,如果它甚至可以在合理的时间内完成
3 回答
hadoop 的想法是它可以帮助您并行化代码执行。如果您只有一台机器,我认为 hadoop 不适合您。由于您有 2 个内核,因此您可以利用 Java 线程。
另一个限制因素是内存。基本上,如果您可以将所有记录提取到内存中,只需在开始计算之前执行此操作。如果不是这种情况(似乎 db 大小超过了您的 RAM 大小),一旦计算线程完成了一些记录,帮助线程就可以将其他记录从数据库中提取到内存中。下面是算法草图:
- 两个 Worker 线程将并行工作(线程数 = CPU 数,因为计算密集型任务)
FirstArray = 在数组或 ArrayList 中加载 10.000,确保您没有使用并发结构。两个线程都会访问这个数组,但是不会改变它。SecondArray 将由 DB Thread 提供(第 3-4 点)。两个线程的 FirstArray 将相同,SecondArray 将不同。你会有嵌套循环:
for (elem1: FirstArray) { for (elem2: SecondArray){ computeSmth(elem1, elem2) if (bestSoFar()) store() } }
一旦 Worker 线程完成,它就会向 BlockingQueue 询问下一部分数据——即新的 SecondArray。
- DB Thread(实际上是第三个线程)将负责从数据库中批量获取数据并填充数组,这些数组将由 Worker Threads 进一步处理。
- 假设第二个表中的 400.000 个元素适合内存。让我们把它分成4个区域。
- 1 个区域将用于第一个线程正在处理的元素,
- 2 区域将用于由第二个线程处理的元素,
- 3 region 是一个等待被 BlockingQueue 中的一个线程占用的数组(容量为 1),
- 4 将用于从数据库中获取的数据,但无法放入队列,因为另一个数组没有被一个工作线程占用。这基本上意味着 DB Thread 会阻塞,直到某个线程从队列中获取下一个数组,这意味着它已经完成了前一个数组并且前一个数组可以被 GCed,这意味着你不会耗尽内存。
- 队列大小可能是基于最大 MySQL 批处理大小、MySQL 检索时间和 Worker 线程在一个批处理上花费的时间的调整主题。
- bestSoFar() 的逻辑应该是经过深思熟虑的,以尽量减少线程同步。
- 基本上,该算法应该可以很好地扩展(每个 CPU 都可以提供接近线性的改进)。
500 万 x 57 双倍数相当于 2 GB 的 RAM。
您拥有的 4 GB 应该不是问题。
为了让事情变得更快,请使用索引。也许也可以实现自己的索引。
或者在适当的地方使用排序。
有十几种方法可以做到这一点。这取决于您需要比较的内容。
一种从两个表中选择最重要列具有相同值的行的方法。比对两个表中的每个匹配行进行比较。
但是,如果匹配是直接转发的(一列是否完全匹配),我会编写一个很好的 SQL 查询,返回每个行组合的匹配列数并选择前 10 个 :)。
我认为,最好的策略是逐一处理 10.000 行,并尝试通过查询找到最佳匹配行,并在 java 中进行额外计算以对最佳行进行排序。
理想情况下,我会为它编写一个 MapReduce 作业。但是,如果您还没有设置它,那么硬编程是您最好的其他选择。