Mapreduce表差异

时间:2013-05-04 18:53:05

标签: sql hadoop mapreduce elastic-map-reduce cascading

我有两个版本(旧/新)的数据库表,大约有100,000,000条记录。它们在文件中:

trx-old
trx-new

结构是:

id date amount memo
1  5/1     100 slacks
2  5/1      50 wine

id是简单的主键,其他字段是非键。我想生成三个文件:

trx-removed (ids of records present in trx-old but not in trx-new)
trx-added   (records from trx-new whose ids are not present in trx-old)
trx-changed (records from trx-new whose non-key values have changed since trx-old)

我需要每天在短批处理窗口中执行此操作。实际上,我需要为多个表和多个模式(为每个模式生成三个文件)执行此操作,因此实际的应用程序更加复杂。但我认为这个例子捕捉到了问题的关键。

这感觉就像mapreduce的一个明显的应用。从未编写过mapreduce应用程序,我的问题是:

  1. 是否有一些EMR应用程序已经这样做了?
  2. 是否有明显的猪或可能是层叠的解决方案?
  3. 还有其他一些非常接近的开源示例吗?
  4. PS我看到了diff between tables问题,但那里的解决方案看起来并不具备可扩展性。

    PPS这是一个演示算法的小玩具:Ruby dbdiff

4 个答案:

答案 0 :(得分:2)

我认为编写自己的工作最简单,主要是因为当典型的reducer只写入一个文件时,你会想要使用MultipleOutputs从单个reduce步骤写入三个单独的文件。您需要使用MultipleInputs为每个表指定映射器。

答案 1 :(得分:1)

我想到的是:

考虑你的表是这样的:

Table_old
1    other_columns1
2    other_columns2
3    other_columns3

Table_new 
2    other_columns2
3    other_columns3
4    other_columns4

追加table_old的元素“a”和table_new的元素“b”。

合并两个文件时,如果第一个文件中存在元素而第二个文件中没有元素,则会将其删除

table_merged
1a    other_columns1
2a    other_columns2
2b    other_columns2
3a    other_columns3
3b    other_columns3
4a    other_columns4

通过该文件,您可以轻松完成操作。

另外,假设你的id是n位数,你有10个簇+ 1个主。您的密钥将是id的第一位数,因此,您将数据均匀地划分为群集。您可以进行分组+分区,以便对数据进行排序。

示例,

table_old
1...0 data
1...1 data
2...2 data

table_new
1...0 data
2...2 data
3...2 data

您的密钥是第一个数字,您根据该数字进行分组,并且您的分区是根据id的其余部分。然后您的数据将作为

进入您的群集
worker1
1...0b data
1...0a data
1...1a data

worker2 
2...2a data
2...2b data and so on.

请注意,a,b不必排序。

修改 合并将是这样的:

FileInputFormat.addInputPath(job, new Path("trx-old"));
FileInputFormat.addInputPath(job, new Path("trx-new"));

MR将获得两个输入,两个文件将合并,

对于附加部分,您应该在Main MR之前再创建两个作业,它们只有Map。第一个Mapappend "a"append "b"添加到第一个列表中的每个元素,第二个Map-Map-Reduce//you have key:Text new Text(String.valueOf(key.toString()+"a")) 添加到第二个列表的元素中。第三个工作(我们现在使用的那个/主要地图)只会减少工作以收集它们。所以你将{{1}}。

可以像那样进行追加

{{1}}

但我认为可能有不同的追加方式,其中一些可能更有效率 (text hadoop

希望它会有所帮助,

答案 2 :(得分:1)

这似乎是级联中要解决的完美问题。您已经提到您从未编写过MR应用程序,如果想要快速入门(假设您熟悉Java),那么Cascading就是去恕我直言的方法。我会在一秒钟内对此进行更多的了解。

可以使用Pig或Hive,但是如果您想对这些列执行额外的分析或更改模式,这些可能不够灵活,因为您可以通过读取列标题或从列标题在Cascading中动态构建模式您创建的映射文件,用于表示架构。

Cascading中你会:

  1. 设置您的传入Taps:点击trxOld并点击trxNew(这些指向您的源文件)
  2. 将您的水龙头连接到Pipes:管道oldPipe和Pipe newPipe
  3. 设置您的传出Taps:点击trxRemoved,点按trxAdded并点按trxChanged
  4. 构建你的管道分析(这是有趣(受伤)发生的地方)
  5. trx-removed: TRX-加入

    Pipe trxOld = new Pipe ("old-stuff");
    Pipe trxNew = new Pipe ("new-stuff");
    //smallest size Pipe on the right in CoGroup
    Pipe oldNnew = new CoGroup("old-N-new", trxOld, new Fields("id1"), 
                                           trxNew, new Fields("id2"), 
                                           new OuterJoin() ); 
    

    外连接为我们提供了NULLS,其他管道(您的源数据)中缺少ID,因此我们可以在后面的逻辑中使用FilterNotNullFilterNull来获取我们的最终管道然后连接到Tap trxRemoved并相应地点击trxAdded。

    <强> TRX-改变

    在这里,我首先使用FieldJoiner连接您正在寻找更改的字段,然后使用ExpressionFilter向我们提供僵尸(因为它们已更改),例如:

    Pipe valueChange = new Pipe("changed");
    valueChange = new Pipe(oldNnew, new Fields("oldValues", "newValues"), 
                new ExpressionFilter("oldValues.equals(newValues)", String.class),
                Fields.All);
    

    这样做是过滤掉具有相同值的字段并保留差异。而且,如果上面的表达式为真,它就会删除该记录。最后,将您的valueChange管道连接到Tap trxChanged,您将拥有三个输出,其中包含您要查找的所有数据,并允许进行一些额外的分析。

答案 3 :(得分:1)

正如@ChrisGerken建议的那样,您必须使用MultipleOutputsMultipleInputs来生成多个输出文件,并将自定义映射器关联到每个输入文件类型(旧/新)。

映射器将输出:

  • key:主键(id)
  • value:从输入文件中记录附加标志(新/旧取决于输入)

reducer将迭代每个键的所有记录R并输出:

  • 删除文件:如果只存在标记为old的记录。
  • 添加文件:如果只存在标记为new的记录。
  • 更改文件:如果R中的记录不同。

由于此算法会根据reducer的数量进行扩展,因此您很可能需要第二个作业,这会将结果合并到单个文件中以进行最终输出。