经常有一些需要做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. RHEL 6.10系统安装配置图解教程

    EL 6.10系统安装配置图解教程(rhel-server-6.5) 截止目前RHEL 6.x最新版本为RHEL 6.10,下面介绍RHEL 6.10的具体安装配置过程,需要的朋友可以参考下 一.安装 ...

  2. springboot 静态资源访问,和文件上传 ,以及路径问题

    springboot 静态资源访问: 这是springboot 默认的静态资源访问路径  访问顺序依次从前到后(http://localhost:8080/bb.jpg) spring.resourc ...

  3. Pattern Evaluation

    对相关性patten质量的常用分析指标有以下这些 其中,X^2 跟 lift不是null-invariant的,也就是说当~A~B项较多时,这两个指标不是很可靠. 据Jiawei Han所言,Kulc ...

  4. [Python]正则匹配字符串 | 蒲公英二维码图片url

    代码示例: import re def Find(string): # findall() 查找匹配正则表达式的字符串 url = re.findall('http[s]?://(?:[a-zA-Z] ...

  5. OSI 的七层模型

    一.概念 概念:开放系统互联参考模型,是由 ISO(国际标准化组织)定义的.目的:规范不同系统的互联标准,使两个不同的系统能够较容易的通讯. 网络刚面世时,通常只有同一家厂商的计算机才能彼此通讯.OS ...

  6. Cgroup与LXC简介

    原文地址: https://blog.51cto.com/speakingbaicai/1359825 一.Docker.LXC.Cgroup的结构关系 根据Docker布道师Jerome Petaz ...

  7. excel怎么设置密码保护?Excel文件添加密码保护教程

    excel怎么设置密码保护?Excel文件添加密码保护教程 众所周知,Excel具有强大的数据处理和数据分析能力,广泛应用于加工学统计及金融统计中.特别是金融统计需要较高的安全性,那么就一定要为Exc ...

  8. sizeof和strlen函数区别

    一.sizeof    sizeof(...)是运算符,在头文件中typedef为unsigned int,其值在编译时即计算好了,参数可以是数组.指针.类型.对象.函数等.    它的功能是:获得保 ...

  9. Java泛型(4):泛型与匿名内部类

    泛型同样也可以使用在匿名内部类中. 下面的例子是对 Java泛型(3):泛型方法 中例(2)的修改. public interface Generator<T> { T next(); } ...

  10. 1. Linux基本操作和基本命令

    常用快捷键: Ctrl + d : 结束符 Ctrl + c : 中断前台进程 Ctrl + z : 将前台进程停止掉   创建终端: 创建终端标签:Ctrl + Shift + t; 切换标签: A ...