如何在hadoop中实现OR join(scalding / cascading)

时间:2012-09-24 22:13:25

标签: scala join hadoop cascading scalding

只需将连接字段作为reducer键发送,就可以轻松地通过单个键连接数据集。 但是,通过几个键来连接记录,其中至少有一个对我来说并不容易。

示例我有日志,我想按用户参数对它们进行分组,我想通过(ipAddress,sessionId,visitorCockies)加入它们

因此,如果log1.ip == log2.ip或log1.session = log2.session或log1.cockie = log2.coockie,log1应与log2分组。也许有可能创建复合键或一些概率方法,如minHash ......

有可能吗?

5 个答案:

答案 0 :(得分:0)

问题在于MapReduce连接通常是通过在某些字段上匹配相同reduce键的记录来实现的,这样它们就会被发送到同一个reducer。因此,解决这个问题的任何事情都会有点破解,但有可能......

以下是我建议的内容:对于每个输入记录,生成三个副本,每个副本都有一个新的“key”字段,该字段以其来自的字段为前缀。例如,假设您有以下输入:

(ip=1.2.3.4, session=ABC, cookie=123)
(ip=3.4.5.6, session=DEF, cookie=456)

然后你会生成

(ip=1.2.3.4, session=ABC, cookie=123, key=ip_1.2.3.4)
(ip=1.2.3.4, session=ABC, cookie=123, key=session_ABC)
(ip=1.2.3.4, session=ABC, cookie=123, key=cookie_123)
(ip=3.4.5.6, session=DEF, cookie=456, key=ip_3.4.5.6)
(ip=3.4.5.6, session=DEF, cookie=456, key=session_DEF)
(ip=3.4.5.6, session=DEF, cookie=456, key=cookie_456)

然后你可以简单地分组这个新领域。

我对scalding / cascading不太熟悉(虽然我的意思是要了解更多关于它的信息),但这肯定符合Hadoop中通常的连接方式。

答案 1 :(得分:0)

按照上面Joe的描述创建单独的连接后,您需要删除重复项。如果在“OR-join”中使用的所有字段中它们相等,则数据中的两个元组是重复的。因此,如果您之后对表示所有相关字段的键执行自然连接,则会将所有重复项组合在一起。因此,您可以通过单个元组的单个替换来替换它们。

让我们看一个例子:假设你有元组字段(A,B,C,D),你感兴趣的字段是A,B和C.你首先要做等分连接分别为A,B和C.对于每一个,您将自己加入初始元组流。用(A0,B0,C0,D0)表示第一个流,用(A1,B1,C1,D1)表示第二个流。结果将是元组(A0,B0,C0,D0,A1,B1,C1,D1)。对于每个元组,您将创建一个元组(A0A1B0B1C0C1,A0,B0,C0,D0,A1,B1,C1,D1),因此所有重复项将在后续的reducer中组合在一起。对于每个组,只返回一个包含的元组。

答案 2 :(得分:0)

你能描述一下“通过几个键加入记录”的更多信息吗?

如果您知道工作流中可以连接特定键的点,可能最好的方法是定义具有多个连接的流,而不是尝试操作复杂的数据结构以便在一个键中解析N个键步骤

这是一个示例应用,它展示了如何在级联中处理不同类型的连接:https://github.com/Cascading/CoPA

答案 3 :(得分:0)

对于级联,我最终创建了一个过滤器,用于检查OR内部任何条件的输出是否为真。级联过滤器输出可选择使用的True / False值。

答案 4 :(得分:0)

提示:使用类型别名使您的Scalding代码更易于阅读

注意0 :这个解决方案特别好用,因为它总是只有1个mapred作业,即使有更多的键可以加入。

注1 :假设每个管道都没有重复的密钥,否则你必须让'key也有一个索引来记录它来自哪个日志,mapTo将是一个flatMapTo和有点复杂。

注意2 :为简单起见,这将丢弃加入字段,以保持它们你需要一个大丑陋的元组(ip1,ip2,session1,session2,...等)。如果你真的想要我可以写出一个保留它们的例子。

注3 :如果你真的想要合并重复的值,你可以用groupBy跟随它,每个logEntry1和logEntry2,产生一个logEntryList,然后cat(如注释中所提到的那样)加入不正常)。这将创建另外两个mapred作业。

type String2 = (String, String)
type String3 = (String, String, String)

def addKey(log: Pipe): Pipe = log.flatMap[String3, String](('ip, 'session, 'cookie) -> 'key)(
  _.productIterator.toList.zipWithIndex.map {
    case (key: String, index: Int) => index.toString + key
  }
)

(addKey(log1) ++ addKey(log2)).groupBy('key)(_.toList[String]('logEntry -> 'group))
.mapTo[Iterable[String], String2]('group -> ('logEntry1, 'logEntry2))(list => (list.head, list.last))