经常有一些需要做id打通的场景,比如用户id打通等,

问题抽象是每条数据都可以解析出一个或多个kv pair:(id_type,id),然后需要将某一个kv pair匹配的多条数据进行merge;

比如:

data1: Array(('type1', 'id1'), ('type2', 'id2'))

data2: Array(('type1', 'id1'), ('type3', 'id3'))

data3: Array(('type2', 'id2'), ('type4', 'id4'))

其中data1和data2通过('type1', 'id1')打通,data1和data3通过('type2', 'id2')打通,最终data1、data2、data3打通成一条数据

data_union: Array(('type1', 'id1'), ('type2', 'id2'), , ('type3', 'id3'), , ('type4', 'id4'))

先定义基础类和方法

  class Data {
def getId : String = ""
} def merge(dataArr : Array[(Map[Byte, String], Data)]) : (Map[Byte, String], Data) = dataArr.head
def generateUUID : String = ""

其中

1)Data表示数据抽象,每条数据都有一个id;

2)Map[Byte, String]表示数据中的kv pair,即 Map[id_type, id]

3)merge将多条数据打通成一条数据;

先看最简单的递归实现

  def unionDataRDD1(rdd : RDD[(Map[Byte, String], Data)]) : RDD[(Map[Byte, String], Data)] = {
var result = rdd.keyBy(_._2.getId).groupByKey.map(item => merge(item._2.toArray)).cache
//Array[id_type]
val idTypes = result.flatMap(item => item._1.keys).distinct.collect
idTypes.foreach(item => result = result.filter(_._1.contains(item)).keyBy(_._1.get(item).get).groupByKey.map(item => merge(item._2.toArray)).union(result.filter(!_._1.contains(item))))
result
}

性能不太好,再看优化后的非递归实现

  def unionDataRDD2(rdd : RDD[(Map[Byte, String], Data)]) : RDD[(Map[Byte, String], Data)] = {
val result = rdd.keyBy(_._2.getId).groupByKey.map(item => merge(item._2.toArray)).cache //((id_type, id), group)
val idGroupRDD = result.flatMap(item => {val uuid = generateUUID; item._1.toArray.map(entry => (entry, uuid))}).cache
//Array(Array(group))
val unionMap = idGroupRDD.groupByKey.map(_._2.toArray.distinct).filter(_.length > 1).collect
//Map(group -> union_group)
.foldLeft(Map[String, String]())((resultUnion, arr) => {
val existingGroupMap = arr.collect({case group : String if resultUnion.contains(group) => (group, resultUnion.get(group).get)}).toMap
if (existingGroupMap == null || existingGroupMap.isEmpty) resultUnion ++ arr.collect({case group : String => (group -> arr.head)}).toMap
else if (existingGroupMap.size == 1) resultUnion ++ arr.collect({case group : String => (group -> existingGroupMap.head._2)}).toMap
else {
val newUnionMap = existingGroupMap.map(_._2).collect({case group : String => (group -> existingGroupMap.head._2)}).toMap
resultUnion.collect({case entry : (String, String) => if (newUnionMap.contains(entry._2)) (entry._1, newUnionMap.get(entry._2).get) else entry}) ++ arr.collect({case group : String => (group -> newUnionMap.head._2)}).toMap
}
}) //((id_type, id), union_group)
val groupMap = idGroupRDD.map(item => (item._1, if (unionMap.contains(item._2)) unionMap.get(item._2).get else null)).filter(_._2 != null).collect.toMap
//(union_group, data)
val groupRDDWithUnion = result.map(item => (item._1.collectFirst({case entry : (Byte, String) if groupMap.contains(entry) => groupMap.get(entry).get}), item)).cache
groupRDDWithUnion.filter(_._1 != None).groupByKey.map(item => merge(item._2.toArray)).union(groupRDDWithUnion.filter(_._1 == None).map(_._2))
}

第二版优化

  def unionDataRDD3(rdd : RDD[(Map[Byte, String], Data)]) : RDD[(Map[Byte, String], Data)] = {
val result = rdd.keyBy(_._2.getId).groupByKey.map(item => merge(item._2.toArray)).cache //((id_type, id), Set[group])
val idGroupArray = result.zipWithUniqueId().flatMap(item => item._1._1.toArray.map(entry => (entry, item._2.toString))).aggregateByKey(Set[String]())((result, item) => result + item, (result1, result2) => result1 ++ result2).collect //Array(Array(group))
val unionMap = idGroupArray.map(_._2).foldLeft(Map[String, String]())((resultUnion, arr) => {
val existingGroupMap = arr.collect({case group : String if resultUnion.contains(group) => (group, resultUnion.get(group).get)}).toMap
if (existingGroupMap == null || existingGroupMap.isEmpty) resultUnion ++ arr.collect({case group : String => (group -> arr.head)}).toMap
else if (existingGroupMap.size == 1) resultUnion ++ arr.collect({case group : String => (group -> existingGroupMap.head._2)}).toMap
else {
val newUnionMap = existingGroupMap.map(_._2).collect({case group : String => (group -> existingGroupMap.head._2)}).toMap
resultUnion.collect({case entry : (String, String) => if (newUnionMap.contains(entry._2)) (entry._1, newUnionMap.get(entry._2).get) else entry}) ++ arr.collect({case group : String => (group -> newUnionMap.head._2)}).toMap
}
}) //(id_type, (id, union_group))
val groupMap = idGroupArray.foldLeft(Map[Byte, Map[String, String]]())((result, item) => if (!result.contains(item._1._1)) result + (item._1._1 -> Map(item._1._2 -> unionMap.get(item._2.head).get)) else result + (item._1._1 -> (result.get(item._1._1).get + (item._1._2 -> unionMap.get(item._2.head).get))))
//(union_group, order)
result.map(item => (item._1.collectFirst({case entry : (Byte, String) if groupMap.contains(entry._1) && groupMap.get(entry._1).get.contains(entry._2) => groupMap.get(entry._1).get.get(entry._2).get}), item)).groupByKey.map(item => merge(item._2.toArray))
}

【原创】大叔案例分享(5)id打通的更多相关文章

  1. 【原创】大叔案例分享(4)定位分析--见证scala的强大

    一 场景分析 定位分析广泛应用,比如室外基站定位,室内蓝牙beacon定位,室内wifi探针定位等,实现方式是三点定位 Trilateration 理想情况 这种理想情况要求3个基站‘同时’采集‘准确 ...

  2. 【原创】大叔案例分享(3)用户行为分析--见证scala的强大

    一 场景分析 用户行为分析应用的场景很多,像线上网站访问统计,线下客流分析(比如图像人脸识别.wifi探针等),比较核心的指标有几个: PV | UV | SD | SC 指标说明: PV(Page ...

  3. ArcGIS Add-in插件开发从0到1及实际案例分享

    同学做毕设,要求我帮着写个ArcGIS插件,实现功能为:遍历所有图斑,提取相邻图斑的公共边长及其他属性(包括相邻图斑的ID),链接到属性表中.搞定后在这里做个记录.本文分两大部分: ArcGIS插件开 ...

  4. Office 2010 KMS激活原理和案例分享

    Office 2010 KMS激活原理和案例分享     为了减低部署盗版(可能包含恶意软件.病毒和其他安全风险)的可能性,Office 2010面向企业客户推出了新的批量激活方式:KMS和MAK.这 ...

  5. Office 2010 KMS激活原理和案例分享 - Your Office Solution Here - Site Home - TechNet Blogs

    [作者:葛伟华.张玉工程师 ,  Office/Project支持团队, 微软亚太区全球技术支持中心 ] 为了减低部署盗版(可能包含恶意软件.病毒和其他安全风险)的可能性,Office 2010面向企 ...

  6. 老李案例分享:MAT分析应用程序服务出现内存溢出过程

    老李案例分享:MAT分析应用程序服务出现内存溢出过程   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loa ...

  7. 老李案例分享:定位JAVA内存溢出

    老李案例分享:定位JAVA内存溢出   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loadrunner的培 ...

  8. [转载]DevOps在传统企业的落地实践及案例分享

    内容来源:2017年6月10日,优维科技高级解决方案架构师黄星玲在“DevOps&SRE 超越传统运维之道”进行<DevOps在传统企业的落地实践及案例分享>演讲分享.IT 大咖说 ...

  9. mysql的"双1设置"-数据安全的关键参数(案例分享)

    mysql的"双1验证"指的是innodb_flush_log_at_trx_commit和sync_binlog两个参数设置,这两个是是控制MySQL 磁盘写入策略以及数据安全性 ...

随机推荐

  1. Python list 遇到的问题

    1.list“+” 运算 <list += > diff. <ndarray +=> list1 += list2是追加,而不是加法运算 list1 = [0,0,0] lis ...

  2. Java-NIO 之 Selector 与 Pipe

    关于阻塞与非阻塞:https://www.cnblogs.com/jhxxb/p/11272727.html 一.传统的 IO 流都是阻塞式的 当一个线程调用 read() 或 write() 时,该 ...

  3. P4095 [HEOI2013]Eden 的新背包问题

    P4095 [HEOI2013]Eden 的新背包问题 题解 既然假定第 i 个物品不可以选,那么我们就设置两个数组 dpl[][] 正序选前i个物品,dpr[][] 倒序选前i个物品 ,价格不超过 ...

  4. bpi English

    一.Marketing and Management Dashboard  营销管理 1.non-stackable voucher 不可累计的券 2.Campaign engine 活动引擎 3.i ...

  5. Kbengine游戏引擎-【5】用Dockerfile打包镜像kbengine

    本文是以docker为例,以ubuntu 16.04做基础镜像 kengine 1.0.0 用supervisor来管理启动 先放下目录结构图: kb--里面放的是kbengine的编译后的引擎以及d ...

  6. Fluent Ribbon Control Suite和AvalonDock 控件库

    Fluent Ribbon Control Suite 是一个Ribbon控件,可以用来创建Office 2010 样式的用户界面,支持MVVM,最近快要更新了,将会有Office 2013 样式的主 ...

  7. Selenium 2自动化测试实战41(多线程技术)

    多线程技术 python通过两个标准库thread和threading提供对线程的支持.thread提供了低级别的,原始的线程以及一个简单的锁.threading基于Java的线程模型设计. 1.th ...

  8. MySQLdb User's Guide

    MySQLdb MySQLdb-1.2.2 API documentation http://mysql-python.sourceforge.net/MySQLdb-1.2.2/ MySQLdb U ...

  9. idea忽略隐藏文件、文件夹的设置操作

    左上角setting 如果要忽略文件夹,则直接填写文件夹名字即可,例如:要忽略target文件夹[建议:尽量不要把target忽略,因为可能编译出问题排查,还需要查看target文件夹中的编译结果] ...

  10. Spring MVC的多视图解析器配置及与Freemarker的集成

    一.从freemarker谈起 Freemarker使用模板技术进行视图的渲染.自从看了Struts标签.Freemarker.JSTL的性能对比后,我毅然决定放弃Struts标签了!效率太差…… S ...