假设我有两个键值数据集 - 数据集A和B,让我们调用它们。我想用Set B中的数据更新Set A中的所有数据,其中两个匹配键。
因为我正在处理如此大量的数据,所以我正在使用Hadoop来实现MapReduce。我担心的是,为了在A和B之间进行这种密钥匹配,我需要将所有Set A(大量数据)加载到每个映射器实例的内存中。这看起来效率很低。
是否有建议的方法来执行此操作,不需要每次都重复在A中加载的工作?
一些伪代码用于澄清我目前正在做的事情:
Load in Data Set A # This seems like the expensive step to always be doing
Foreach key/value in Data Set B:
If key is in Data Set A:
Update Data Seta A
答案 0 :(得分:3)
根据文档,MapReduce框架包括the following steps:
您已经描述了一种执行连接的方法:在每个Mapper中将所有Set A加载到内存中。你说这是低效率的。
相反,如果两个集合都按键排序和分区,请注意可以将大型连接划分为任意多个较小的连接。 MapReduce在上面的步骤(2)中按键对每个Mapper的输出进行排序。然后按键对排序的映射输出进行分区,以便为每个Reducer创建一个分区。对于每个唯一键,Reducer将接收来自Set A和Set B的所有值。
要完成连接,Reducer只需输出密钥和Set B中的更新值(如果存在);否则,从Set A输出密钥和原始值。要区分Set A和Set B中的值,请尝试在Mapper的输出值上设置一个标志。
答案 1 :(得分:3)
到目前为止发布的所有答案都是正确的 - 这应该是Reduce-side join ...但是没有必要重新发明轮子!您是否考虑过此Pig,Hive或Cascading?它们都内置了连接,并且进行了相当好的优化。
答案 2 :(得分:2)
This video tutorial描述了如何通过MapReduce进行大规模加入,从12分钟左右开始。
以下是他将文件B中的记录连接到密钥K上的文件A的记录中的基本步骤,使用伪代码。如果这里的任何内容都不清楚,我建议观看视频,因为他的解释能力比我做得好得多。
在你的Mapper中:
K from file A:
tag K to identify as Primary Key
emit <K, value of K>
K from file B:
tag K to identify as Foreign Key
emit <K, record>
编写一个将忽略PK / FK标记的Sorter和Grouper,这样您的记录就会被发送到同一个Reducer,无论它们是PK记录还是FK记录,并且组合在一起。
写一个比较器,它将比较PK和FK键并首先发送PK。
此步骤的结果将是具有相同键的所有记录将被发送到同一个Reducer并且在同一组值中将被减少。标记为PK的记录将首先出现,然后是来自B的所有需要加入的记录。现在,减速器:
value_of_PK = values[0] // First value is the value of your primary key
for value in values[1:]:
value.replace(FK,value_of_PK) // Replace the foreign key with the key's value
emit <key, value>
结果将是文件B,其中所有出现的K都替换为文件A中的K值。您还可以扩展它以实现完整的内部联接,或者将这两个文件全部写出来直接数据库存储,但是一旦你开始工作,这些都是非常简单的修改。