flink和spark处理数据方式区别,spark和flink的基本数据模型
今天是星火特辑第四篇。让我们来看看一对RDD。
定义
在以前的文章中,我们熟悉RDD的概念,以及RDD的基本变换和行为操作。今天,让我们来看看RDD非常常见的PairRDD。也被称为键值对RDD,可以理解为KVRDD。
我很了解KV。是键和值的组合。比如python dict或者C和Java映射的基本元素就是键值对。与以前的基本RDD相比,pariRDD支持更多的操作,并提供相对更灵活和复杂的功能。例如,可以根据键进行聚合,或者计算交点。
所以pairRDD的数据类型只是KV结构的RDD,内涵不是很丰富,不用担心。
Pairdd转换操作
RDD对也是一个RDD,所以前面描述的RDD转换操作当然也可以使用。两者的关系类似于类继承的关系。RDD是父类,而RDD对是实现一些新功能的子类。子类可以调用父类中的所有方法,但是父类不能调用子类中的方法。
打电话的时候要注意。我们对RDD的数据格式是KV的二进制群,所以我们传递的函数一定是二进制群数据的函数。否则,操作结果可能会有问题。以下是一些最常见的转换操作。
为了简化演示,您可以使用固定的RDD来执行各种转换操作,并直观地理解这些转换操作是干什么的。
ex1=sc.parallelize ([ 1,2 ]、[ 3,4 ]、[ 3,5 ]).
复制代码
键、值和排序键
这三种变换操作应该是最常见最简单的,简单到我们可以从字面上猜出意思。
让我们先来看看键和值:
在我们的RDD中,二项式组的第一个元素被设置为key,第二个元素被设置为value。注意,它不是映射或字典,所以键和值都可以重复。
SortByKey也很直观。从字面上看,可以看出RDD的数据是按照键值排序的。同样,看看下面的结果。
映射值和平面映射值
MapValues不能直接使用,必须作为参数函数传递。这意味着对所有值运行该函数。例如,如果要将所有值转换为字符串,可以执行以下操作:
flatMapValues的操作与我们的认知略有相悖。我们知道flatMap操作可以分解嵌套数组,但是如何分解嵌套value呢?说到这里,我们的值不一定是数组。这是我们收到的函数的描述。这个计划的操作实际上是函数返回的结果。也就是函数返回迭代器,分散的内容其实就是这个迭代器里的值。
这样表达对我来说可能有点无聊,但就看一个例子:
不知道这个结果是否出乎意料,但整个过程就是这样。调用flatMapValues操作返回迭代器。迭代器的内容是范围(x,x ^ 3)。实际上每个键对应这样一个迭代器,然后迭代器的内容被打散,和键组成一个新的对。
groupByKey,reduceByKey,foldByKey
这两个功能也很接近,不过我先说第一个。学习SQL的同学应该很了解group by operation的含义。不知道也没关系。group by可以很容易理解为分成合并或桶。也就是说,如果具有相同键值的值被组合成一个,则获得键列表的RDD对,并且具有相同键值的值被放入一个列表中。
请看看下面的例子:
因为我们调用groupby的结果是一个对象,所以我们必须调用mapValues并将其转换为一个列表才能使用它。否则,无法通过collect获取。
ReduceByKey类似于groupByKey,只是groupByKey是合并的。但是,reduceByKey被传递给reduce函数,这是reduce执行的结果。请看一个例子:
在这个例子中,执行累积加法,并且添加具有相同key值的值。
foldByKey和fold的使用方法差别不大,唯一的区别是增加了基于键值的聚合逻辑。如果将分区的初始值设置为0,则与reduceByKey几乎相同。
只要你明白foldByKey的初始值是以partition为对象的。
按键组合
这也是最核心、最难以理解的转换操作,但我们先来看看它的参数。总共接受五个参数。首先,对我们每个人来说
第一个参数是createCombiner。
它的作用是根据我们的需要对值进行初始化和初始化,比如将字符串类型转换为int或者其他操作。我们可以用标记把它写成V=C,其中V是值,C是我们初始化后的新值。
它会和value一起作为新对传入第二个函数,所以第二个函数接受的参数是(C,V)的二元组。我们要做的就是定义这个二元组的并集,所以第二个函数可以写成(C,v)=c,源代码和在线教程里的注释都是这样写的,但是我觉得可能因为出现了两个C,所以大家很难理解,我觉得可以写成(C,V)=D,这样更好。
最后一个函数是组合d,所以可以写成(d,d)=d。
至此,我们似乎明白了它的原理,但似乎有很多问号,总觉得有些地方不太对劲。我想了很久才找到问题的根源。合并的原因是什么?有没有发现第二个函数和第三个函数都是用来合并的?为什么要两次合并?两者有什么区别?如果这个问题不理解,那么它的使用一定是错误的。个人认为这个问题是这次转换操作的核心。没有把这个问题解释清楚的博客是不够清楚的。
其实这两个合并的逻辑差不多,只是合并的范围不同。第一次合并是为了分区,第二次合并是为了key。在spark中,数据可能存储在多个分区中,所以我们必须合并两次。第一次,我们在分区内集成数据,第二次,我们跨分区合并数据。因为不同分区的数据可能相隔很远,网络传输的时间会太长,所以我们希望传输的数据越少越好,这也是为什么要用两次groupby的原因。
我们再举一个例子:
在本例中,我们计算了每个单词的平均出现次数。我们一点一点来看。首先,我们的第一个函数将value转换成一个(1,value)元组。元组的第0个元素表示单词出现的文档数,第1个元素表示在文档中出现的次数。所以第二个函数,也就是组内聚合的函数,我们只需要把出现的文档数加1,累加出现的次数。因为这次聚合的对象都是(1,value)类型的元素,也就是聚合前没有结果。
在第三个函数中,我们还会累计出现的总次数,因为这个函数处理的结果是每个分区都被聚合了一次。比如苹果在一个分区的两个文档中出现,一共20次,在一个分区的三个文档中出现,一共30次,那么显然我们在五个文档中出现,一共50次。
因为我们要计算平均值,所以我们必须用出现的总次数除以出现的文档数。最后,map之后,因为得到的还是二元组,所以不能直接采集,需要使用collectAsMap。
让我们用图表来展示上面的例子,这样就很容易理解了:
连接操作
在spark中,除了基本的转换操作,spark还提供了额外的连接操作来配对RDD。通过连接,我们可以轻松地像操作一套RDD。操作方法也很简单,类似于SQL中操作数据表的形式,即join操作。连接操作可分为连接(内连接)、左连接和右连接。
如果熟悉SQL的话,三者的区别应该非常清楚。它与SQL中的join相同。不熟也没关系,解释起来也不复杂。在连接时,我们经常用一个表连接另一个表,就像两个数相减,我们用一个数减去另一个数。比如A.join(B),我们称A为左表,B为右表。所谓联接,就是将两个表中的某个字段或某些值相同的行联接起来。
例如,一个表单是学生表单,另一个是考勤表单。只要我们的两个表与学生的id链接,我们就可以获得学生的出勤记录。但既然是集合关联,就会出现数据无法关联的情况。例如,一个学生没有上学,或者出席名单中的学号是错误的。我们有四种方法来处理数据无法关联的情况。第一种方法是丢弃所有的数据,丢弃不能关联的数据。第二种方式是保留所有字段,不能关联的字段记录为NULL。三是左表无法关联的预留,右表丢弃。四是保留右表,丢弃左表。
下图就是这四种join,非常形象。
我们来看一些实际例子来体验一下。
首先创建一个数据集:
ex1=sc.parallelize([[frank ,30],[bob ,9],[silly ,3]])
ex2=sc.parallelize([[弗兰克,80],[鲍勃,12],[玛丽,22],[弗兰克,21],[鲍勃,22]])
复制代码
接下来,我们分别运行这四种联接,观察联接后的结果。
从结果中我们可以看到,如果两个数据集中有多个键值相同的数据,spark会将它们成对相乘以进行匹配。
行动
最后,让我们来看看RDD的行动。配对rdd也是一个rdd,因此适用于普通RDD的操作也适用于配对rdd。但除此之外,spark还为其开发了独特的移动操作。
计数键
CountByKey顾名思义就是根据键值计算每个键值出现的次数,相当于count groupby的SQL语句。让我们看一个具体的例子:
收藏地图
这也很好理解。实际上,它意味着最终结果以地图的形式输出:
从返回的结果可以看出,输出是dict类型的。也就是Python中的‘地图’。
检查
这个词看起来很少见,实际上是指根据键值找到对应的值。也就是常用的get函数。当我们传入一个键值时,它会自动返回该键值对应的所有值。如果有多个值,将返回列表。
摘要
至此,与配对RDD相关的所有操作都已介绍完毕。Pair rdd在我们日常使用中出现的频率非常高,实现一些复杂的操作非常方便。
另外,今天的文章包含了很多内容,要彻底理解还需要一点努力。这个不用看文章就能做到,不过没关系。刚开始学的时候,我们只需要对这些API和使用方法有个大概的印象。具体使用细节,我们在使用的时候可以查阅相关资料。
今天的文章到此为止。如果你认为你有所收获,请关注或转发。你的一点点努力对我很重要。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。