RDD基础:
transformation操作:由一个RDD生成新的RDD, 转化操作是惰性的
action操作:由RDD生成其他数据类型,向驱动器返回结果或者把结果写入外部文件系统。
默认情况下,RDD会在每次进行action操作时重新计算。
常用转化操作:
每次针对一个元素的操作:
filter:过滤出符合条件的数据,蚕食不改变输入的原始数据。
map:对每个元素返回一个输出元素,返回类型不需要喝输入的类型一样。
flatMap:对每个输入元素生成多个输出元素,返回值是一个序列的迭代器,输出的RDD倒不是由迭代 器组成的,我们得到的是一个包含各个迭代器可访问的所有元素的RDD。
(理解map和flatMap的区别:map输出的元素个数和输入的个数相同,但是每个元素的类型可能变了,比如由一个String变成了一个String数组。而flatMap将这些元素拍扁,这样个数可能变多了。)
伪集合操作:
RDD本身不是严格意义上的集合,但是支持数学上的集合操作,比如合并和相交操作,但是这些操作要求RDD是相同数据类型的。
distinct:去除重复元素, 为了确保元素只有一份,所以要经过网络混洗,因此开销比较大。
union:返回一个包含两个RDD中所有元素的RDD。不需网络混洗。
intersection:返回两个RDD中都有的元素。需网络混洗。
subtract:返回只存在第一个RDD而不存在第二个RDD中的所有元素组成的RDD。需网络混洗。
cartesian:返回两个RDD的笛卡尔积。
常用行动操作:
行动操作:
take:获取RDD中少量的元素到本地。
top:获取前几个元素。
collect:获取整个RDD中的元素到本地,因此数据规模大的时候不宜使用。
reduce:将操作RDD的两个元素并返回同样类型的一个元素。要求输入和输出类型一样。
fold:和reduce一样,外加一个计算的初始值。要求输入和输出类型一样。
aggregate:可以代替map后接fold方式,不要求输入和输出类型一样。
takeSample:从数据中获取一个采样,并指定时候替换。
count:计算RDD中元素个数。
countByValue:各元素在RDD中出现的次数。
foreach:不论什么情况,都可以使用foreach行动操作来对RDD中的每个元素进行操作,而不需要把RDD发回本地。
键值对操作:
键值对RDD通常用来进行聚合计算。当需要把一个普通的RDD转为par RDD时,可以调用map函数实现。不论是基础RDD的转化操作还是行动操作,在pair RDD上同样可用。
常用动机操作:
1.pair RDD动机操作:
countByKey:对每个键对应的元素分别计数。
collectAsMap:将结果以映射表的形式返回,以便查询。
lookup:返回制定键对应的所有值。
常用转化操作:
1.每次针对一个元素的操作:
reduceByKey:合并具有相同键的值。
groupByKey:对具有相同键的值进行分组。
combineByKey:使用不同的返回类型合并具有相同键的值。
mapValues:对pair RDD中的每个值应用一个函数而不改变键。
flatMapValues:对pair RDD中的每个值应用flatMap,然后将返回的每个元素都生成一个对应原键的键值对纪录。
keys:返回一个仅包含键的RDD。
values:返回一个仅包含值的RDD。
sortByKey:返回一个根据键排序的RDD。
2.针对两个pair RDD的转化操作。
subtractByKey:删除第一个pair RDD中与第二个pair RDD中键相同的元素。
join:对两个pair RDD进行内链接。
rightOuterJoin:对两个pair RDD进行右外链接。确保第二个RDD键一定存在。
leftOutJoin:对两个pai RDD进行左外链接。确保第一个RDD键一定存在。
cogroup:将两个RDD中拥有相同键的数据分组到一起。
向spark传递函数时需要注意:
python不要传某个对象的成员活着对某个对象的一个字段的引用,这样spark会将整个对象都发到工作节点上,高效的方式的用局部变量记录需要传的值。
scala也同样存在上述问题。解决办法也是用局部变量接收需要传的字段。
持久化:
scala和java中,默认情况下persist会把数据以序列化的形式缓存在JVM的堆空间中,在python中,会始终序列化存储的数据,所以持久化级别默认值就是以序列化后的对象存储在jvm堆空间中。当把数据写到磁盘或者堆外存储时,也总是使用序列化后的数据。
如果缓存的数据太多,内存中放不下,spark会自动利用最近最少使用的缓存策略把最老的分区从内存中移除。
分区:
pair RDD都可以进行分区,可以调用partitionBy指定分区方式,partitionBy是一个转化操作,因此它不改变原来的RDD,而是返回一个新的RDD。当调用partitionBy之后记得进行persist,否则分区操作带来的好处将被抵消。
能从分区中获得好处的操作包括:cogroup, groupWith, join, leftOuterJoin, rightOuterJoin, groupByKey, reduceByKey, combineByKey, lookup。
对于二元操作,输出数据分区方式取决于父RDD的分区方式,默认情况下,结果会采用哈希分区,如果父RDD以设置过分区方式,那么结果将采用那种分区方式,如果两个RDD都设置过分区方式,结果RDD将采用第一个父RDD的分区方式。
Spark运行时架构:
Spark集群采用的是主/从结构,中央协调节点称为驱动器节点,与之对应的工作节点被称为执行器节点(驱动器节点和执行器节点都是逻辑概念)。Spark应用通过一个叫做集群管理器(例如spark自带的独立集群管理器,Yarn,Mesos等。)的外部服务在集群中的机器上启动。
驱动器节点:
执行main方法的进程,它执行用户编写的用来创建SparkContext,创建RDD,以及进行RDD的转化和行动操作的代码。(注意,具体的RDD task在执行器里执行。)
职责:1.将用户程序转为任务。
2.对执行器节点调度任务。
执行器节点:
作用:1.负责运行组成Spark应用的任务,并将结果返回给驱动器进程。
2.通过自身的块管理器为用户程序中要求缓存的RDD提供内存式存储。
集群上运行spark程序的详细过程:
1. 用户通过spark-submit脚本提交应用。
2. spark-submit脚本启动驱动器程序,调用用户自定义的main方法。
3. 驱动器程序与集群管理器通信,申请资源以启动执行器结点。
4. 集群管理器为驱动器程序启动执行器结点。
5. 驱动器进程执行用户应用中的操作,根据程序中所定义的对RDD的转化和行动操作,驱动器节点把工作以任务的形式发送到执行器进程。
6. 任务在执行器程序中进行计算并保存结果。
7. 如果驱动器程序的main方法退出,或者调用了SparkContext.stop,驱动器程序会终止执行器进程,并且通过集群管理器释放资源。