RDD编程基础


PySpark 启动

一、PySpark 资源导航

Ref: Spark与Python结合:PySpark初学者指南

Ref: Spark 编程指南简体中文版

二、SparkContext

SparkContext 的作用

用Python来连接Spark:可以使用RD4s并通过库Py4j来实现。

PySpark Shell将Python API链接到Spark Core并初始化Spark Context。(SparkContext是Spark应用程序的核心)

1.Spark Context设置内部服务并建立到Spark执行环境的连接。

2.驱动程序中的Spark Context对象协调所有分布式进程并允许进行资源分配。

3.集群管理器执行程序,它们是具有逻辑的JVM进程。

4.Spark Context对象将应用程序发送给执行者。

5.Spark Context在每个执行器中执行任务。

SparkContext 初始化

统计带有字符“a”或“b”的行数。

from pyspark import SparkContext
sc = SparkContext("local", "first app") logFile = "file:///home/hadoop/spark-2.1.0-bin-hadoop2.7/README.md"
logData = sc.textFile(logFile).cache()
numAs = logData.filter(lambda s: 'a' in s).count()
numBs = logData.filter(lambda s: 'b' in s).count()
print "Lines with a: %i, lines with b: %i" % (numAs, numBs)

三、SparkConf

SparkConf包含了Spark集群配置的各种参数。

from pyspark import SparkConf, SparkContext
conf = SparkConf().setAppName("PySpark App").setMaster("spark://master:7077") sc = SparkContext(conf=conf)

以下是SparkConf最常用的一些属性

    • set(key,value) - 设置配置属性。

    • setMaster(value) - 设置主URL。

    • setAppName(value) - 设置应用程序名称。

    • get(key,defaultValue = None) - 获取密钥的配置值。

    • setSparkHome(value) - 在工作节点上设置Spark安装路径。

RDD创建

一、加载文件

.textFile()方法从三个方式读取内容:HDFS, LOCAL, S3

Local

读取本地文件,生成一个RDD (就是lines)。

# RDD lines变量
lines = sc.textFile("file:///usr/local/spark/mycode/rdd/word.txt")  # 三个斜杠 lines.foreach(print)
lines.first() lines.saveAsTextFile("...")  # 把RDD写入到文本文件中

HDFS

以下三个等价。为了区别本地读取,读方式采用了三斜杠。

lines = sc.textFile("hdfs://localhost:9000/user/hadoop/word.txt")
lines = sc.textFile("/user/hadoop/word.txt")
lines = sc.textFile("word.txt") lines.saveAsTextFile("writeback")  # 把RDD写入到HDFS文件中

addFile函数

原文链接:https://blog.csdn.net/guohecang/article/details/52095387

在Apache Spark中,您可以使用 sc.addFile 上传文件(sc是您的默认SparkContext),并使用 SparkFiles.get 获取工作者的路径。

我们在使用Spark的时候有时候需要将一些数据分发到计算节点中。

(1)一种方法是将这些文件上传到HDFS上,然后计算节点从HDFS上获取这些数据。

(2)我们也可以使用addFile函数来分发这些文件。

注意,如果是spark程序通过yarn集群上加载配置文件,path必须是集群hdfs的绝对路径,如:viewfs://58-cluster//home/hdp_lbg_supin/resultdata/zhaopin/recommend/config/redis.properties。

from pyspark import SparkContext
from pyspark import SparkFiles finddistance = "/home/hadoop/examples_pyspark/finddistance.R"
finddistancename = "finddistance.R" sc = SparkContext("local", "SparkFile App")
sc.addFile(finddistance)
print "Absolute Path -> %s" % SparkFiles.get(finddistancename)

二、通过并行集合列表创建RDD

每个元素i相当与一行。

array = [1,2,3,4,5]
rdd = sc.parallelize(array)
rdd.foreach(print)

序列化

序列化用于Apache Spark的性能调优。

通过 "网络发送" 或 "写入磁盘" 或 "持久存储在内存中" 的所有数据都应序列化。

PySpark支持用于性能调优的自定义序列化程序。

from pyspark.context import SparkContext
from pyspark.serializers import MarshalSerializer sc = SparkContext("local", "serialization app", serializer = MarshalSerializer())
print(sc.parallelize(list(range(1000))).map(lambda x: 2 * x).take(10)) sc.stop()

RDD操作

[Transformation操作]

对一个数据为{1, 2, 3, 3}的RDD进行基本的RDD转化操作

对数据分别为{1, 2, 3}和{3, 4, 5}的RDD进行针对两个RDD的转化操作

[Action操作]

对一个数据为{1, 2, 3, 3}的RDD进行基本的RDD行动操作

一、"转换" 操作

filter(func)

lines = sc.textFile("file:///<path>")
linesWithSpark = lines.filter(lambda line: "Spark" in lines)
linesWithSpark.foreach(print)

map(func)

data = [1,2,3,4,5]
rdd1 = sc.parallelize(data)
rdd2 = rdd1.map(lambda x: x+10)
rdd2.foreach(print)

flatMap

所谓flat,就是最后要的是 “单词的集合”。

lines = sc.textFile("file:///usr/local/spark/mycode/rdd/word.txt")
words = lines.flatMap(lambda line:line.split(" "))

流程本质上就是:[[...], [...], [...]] --> flat降维 --> [...]

groupByKey

根据key把value归并起来。

words = sc.parallelize([("Hadoop",1), ("is",1), ...)

words1 = words.groupByKey()
words1.foreach(print)

下图左边的输入,可以通过map(lambda word: (word,1))来获得。

reduceByKey

进一步地,直接将groupByKey的values经过reduce处理后可变为一个值。

二、"行动" 操作

惰性机制。

rdd = sc.parallelize([1,2,3,4,5])

rdd.count()

rdd.take(3)    # 以数组的形式返回数据集中的前n个元素

rdd.reduce(lambda a,b:a+b)

rdd.collect()  # 以数组的形式返回数据集中的所有元素

rdd.foreach(lambda elem:print(elem))

三、RDD常见操作

(1) RDD代表 Resilient Distributed Dataset,它们是在多个节点上运行和操作以在集群上进行并行处理的元素。

from pyspark import SparkContext
sc = SparkContext("local", "count app")
words = sc.parallelize (
["scala",
"java",
"hadoop",
"spark",
"akka",
"spark vs hadoop",
"pyspark",
"pyspark and spark"]
)

方法测试:

>>> words_filter = words.filter(lambda x: 'spark' in x)
>>> filtered = words_filter.collect()
>>> print("Fitered RDD -> %s" % (filtered))
Fitered RDD -> ['spark', 'spark vs hadoop', 'pyspark', 'pyspark and spark'] >>> words_map = words.map(lambda x: (x, 1))
>>> mapping = words_map.collect()
>>> print("Key value pair -> %s" % (mapping))
Key value pair -> [('scala', 1), ('java', 1), ('hadoop', 1), ('spark', 1), ('akka', 1), ('spark vs hadoop', 1), ('pyspark', 1), ('pyspark and spark', 1)] >>> words.cache()
ParallelCollectionRDD[0] at parallelize at PythonRDD.scala:195
>>> caching = words.persist().is_cached
>>> print("Words got chached > %s" % (caching))
Words got chached > True

(2) 这个也是非常类似与python中的api。

from pyspark import SparkContext
from operator import add
sc = SparkContext("local", "Reduce app")
nums = sc.parallelize([1, 2, 3, 4, 5])

方法测试: 

>>> adding = nums.reduce(add)
>>> print("Adding all the elements -> %i" % (adding))
Adding all the elements -> 15

(3) 类似sql中的join。

from pyspark import SparkContext
sc = SparkContext("local", "Join app")

方法测试:

>>> x = sc.parallelize([("spark", 1), ("hadoop", 4)])
>>> y = sc.parallelize([("spark", 2), ("hadoop", 5)])
>>> joined = x.join(y)
>>> joined
PythonRDD[16] at RDD at PythonRDD.scala:53  # 说明joined是个惰性的rdd。
>>> final = joined.collect()
>>> print("Join RDD -> %s" % (final))
Join RDD -> [('hadoop', (4, 5)), ('spark', (1, 2))]

四、“持久化” 的必要性

没用持久化

list = ["Hadoop”, “Spark", "Hive"]
rdd = sc.parallelize(list)
print(rdd.count())
# 动作操作,触发一次从头到尾的计算 print(','.join(rdd.collect()))  # 以逗号作为分隔把这三个字符串连接起来,python使用;
# 动作操作,触发一次从头到尾的计算

使用持久化

.persist(MEMORY_AND_DISK)

.persist(MEMORY_ONLY) ---> .cache() # 简洁写法

list = ["Hadoop”, “Spark", "Hive"]
rdd = sc.parallelize(list) # 第一次行动计算时,才真正的缓存持久化
rdd.cache()

print(rdd.count())
print(','.join(rdd.collect()))  # 这里便不需要在从头到尾计算,因为rdd已缓存 rdd.unpersist()

原始的方式,如下所示:

StorageLevel决定如何存储RDD。在Apache Spark中,StorageLevel决定RDD是应该存储在内存中还是存储在磁盘上,或两者都存储。它还决定是否序列化RDD以及是否复制RDD分区。

让我们考虑以下StorageLevel示例,其中我们使用存储级别 MEMORY_AND_DISK_2, 这意味着RDD分区将具有2的复制。

from pyspark import SparkContext
import pyspark sc = SparkContext (
"local",
"storagelevel app"
)
rdd1 = sc.parallelize([1,2]) rdd1.persist( pyspark.StorageLevel.MEMORY_AND_DISK_2 )
rdd1.getStorageLevel()
print(rdd1.getStorageLevel())

RDD分区

Ref: Spark-RDD 分区

一、分区好处

增加并行性

多节点同时计算。

减少通信开销

(UserId, UserInfo) join (UserID, LinkInfo) ---> (UserID, UserInfo, LinkInfo)

一个文件非常大,分块存储在不同的机器上,谓之 “分块”

左图:每一个块,有所有rows的一部分信息;

右图:没一个块,只有一部分rows的信息。

左图三步骤:

(1) join操作会将两个数据集中的所有的键的哈希值都求出来,

(2) 将哈希值相同的记录传送到同一台机器上,

(3) 之后在该机器上对所有键相同的记录进行join操作。

这种情况之下,每次进行join都会有数据混洗的问题,造成了很大的网络传输开销。

右图三过程:

(1) 由于UserData表比events表要大得多,所以选择将UserData进行分区。

(2) 之后Spark就会知晓该RDD是根据键的哈希值来分区的。

(3) 这样在调用join()时,Spark就会利用这一点。当调用UserData.join(events)时,Spark只会对events进行数据混洗操作,将events中特定的UserID的记录发送到userData的对应分区所在的那台机器上。

二、分区原则

手动分区

尽量等于集群中的逻辑cpu core的数量。

弹性RDD的演示:

list = [1,2,3,4,5]

data = sc.parallelize(list, 2)
len(data.glom().collect())
2 rdd = data.repartition(1)
len(rdd.glom().collect())
1

自定义分区

三种分区方式:

  • HashPartitioner(默认)
  • RangePartitioner(默认)
  • 自定义分区

分配分区的index。

from pyspark import SparkConf, SparkContext

def MyPartitioner(key):
  print("MyPartitioner is running")
  print('The key is %d' % key)
  return key % 10

自定义分区。

def main():
  print("The main function is running")
  conf = SparkConf().setMaster("local").setAppName("MyApp")
  sc = SparkContext(conf = conf)   # 把这些数字分成5个分区
  data = sc.parallelize(range(10), 5)   data.map(lambda x: (x,1))  \
    .partitionBy(10, MyPartitioner)  \
    .map(lambda x: x[0])  \
    .saveAsTextFile("file:///usr/local/spark/mycode/rdd/partitioner")  # 目录地址,10个分区是10个文件 if __name__ == '__main__':
  main()

map(lambda x: (x, 1))

map(lambda x: x[0])

三、调试方式

因为分了10个区,所以最后生成了10个文件。

# python sol
python3 TestPartitioner.py

# spark sol
spark-submit TestPartitioner.py

栗子:word count

一、进行词频统计

其实就是上文中RDD操作的一个综合应用。

lines = sc.textFile("file:///usr/local/spark/mycode/rdd/word.txt")
wordCount = lines.flatMap(lambda line: line.split(" "))
.map(lambda word: (word,1))
.reduceByKey(lambda a,b:a+b)

print(wordCount.collect())

二、过程解析

将统计内容分配到各个节点,计算出分区的统计结果,之后再reduce到master统计出最终结果。

键值对RDD

一、如何创建

从文件中加载

flatMap, map即可得。

通过并行集合创建

直接flat即可。

二、如何转换

常见的转换方法如下。

reduceByKey(lambda a,b: a+b)
# 等价于:
groupByKey().map(lambda t: (t[0], sum(t[1])))  # (one,) (two,(1,1)) (three,(1,1,1)) pairRDD.keys().foreach()
pairRDD.values().foreach()

pairRDD.sortByKey().foreach()   # 降序排序sortByKey(False) d1.reduceByKey(lambda a,b: a+b).sortBy(lambda x: x,False)   # 默认是key排序
d1.reduceByKey(lambda a,b: a+b).sortBy(lambda x: x[0],False)  # key排序
d1.reduceByKey(lambda a,b: a+b).sortBy(lambda x: x[1],False)  # value排序

pairRDD.mapValues(lambda x: x+1)  # 只针对dict的value操作 pairRdd1.join(pairRDD2)   # 根据key,把value归并起来,类似于:flatMap+map

栗子:average sales

一、计算每天平均销量

二、代码

x[0]代表值的总和。

x[1]代表值的个数。

rdd = sc.parallelize([("spark",2), ("hadoop", 6), ("hadoop", 4), ("spark", 6)])

rdd.mapValues(lambda x: (x,1)). \
... reduceByKey(lambda x,y: (x[0]+y[0], x[1]+[1]). \
... mapValues(lambda x: x[0]/x[1]).collect()

广播与累积器

一、共享变量

默认情况下,如果在一个算子的函数中使用到了某个外部的变量,那么这个变量的值会被拷贝到每个task中。此时每个task只能操作自己的那份变量副本。如果多个task想要共享某个变量,那么这种方式是做不到的。

因此,Spark提供了两种共享变量,一种是Broadcast Variable(广播变量),另一种是Accumulator(累加变量)。

  • Broadcast Variable会将使用到的变量,仅仅为每个节点拷贝一份,而不会为每个task都拷贝一份副本。更大的用处是优化性能,减少网络传输以及内存消耗。
  • Accumulator则可以让多个task共同操作一份变量,主要可以进行累加操作。

二、实例演示

Broadcast

广播变量用于跨所有节点保存数据副本。此变量缓存在所有计算机上,而不是在具有任务的计算机上发送。

class pyspark.Broadcast (
sc = None,
value = None,
pickle_registry = None,
path = None
)

示例代码:

from pyspark import SparkContext
sc = SparkContext("local", "Broadcast app")
words_new = sc.broadcast(["scala", "java", "hadoop", "spark", "akka"])
data = words_new.value
print "Stored data -> %s" % (data)
elem = words_new.value[2]
print "Printing a particular element in RDD -> %s" % (elem)

Accumulator

累加器变量用于通过关联和交换操作聚合信息。例如,您可以使用累加器进行求和操作或计数器(在MapReduce中)。

from pyspark import SparkContext
sc = SparkContext("local", "Accumulator app")
num = sc.accumulator(10)
def f(x):
global num
num+=x
rdd = sc.parallelize([20,30,40,50])
rdd.foreach(f)
final = num.value
print "Accumulated value is -> %i" % (final)

End.

[Spark] 02 - Practice Spark的更多相关文章

  1. Spark快速入门 - Spark 1.6.0

    Spark快速入门 - Spark 1.6.0 转载请注明出处:http://www.cnblogs.com/BYRans/ 快速入门(Quick Start) 本文简单介绍了Spark的使用方式.首 ...

  2. 【译】Spark官方文档——Spark Configuration(Spark配置)

    注重版权,尊重他人劳动 转帖注明原文地址:http://www.cnblogs.com/vincent-hv/p/3316502.html   Spark主要提供三种位置配置系统: 环境变量:用来启动 ...

  3. 一、spark入门之spark shell:wordcount

    1.安装完spark,进入spark中bin目录: bin/spark-shell   scala> val textFile = sc.textFile("/Users/admin/ ...

  4. Spark学习之Spark Streaming

    一.简介 许多应用需要即时处理收到的数据,例如用来实时追踪页面访问统计的应用.训练机器学习模型的应用,还有自动检测异常的应用.Spark Streaming 是 Spark 为这些应用而设计的模型.它 ...

  5. Spark学习之Spark调优与调试(二)

    下面来看看更复杂的情况,比如,当调度器进行流水线执行(pipelining),或把多个 RDD 合并到一个步骤中时.当RDD 不需要混洗数据就可以从父节点计算出来时,调度器就会自动进行流水线执行.上一 ...

  6. Spark学习之Spark调优与调试(一)

    一.使用SparkConf配置Spark 对 Spark 进行性能调优,通常就是修改 Spark 应用的运行时配置选项.Spark 中最主要的配置机制是通过 SparkConf 类对 Spark 进行 ...

  7. 【Spark篇】---Spark调优之代码调优,数据本地化调优,内存调优,SparkShuffle调优,Executor的堆外内存调优

    一.前述 Spark中调优大致分为以下几种 ,代码调优,数据本地化,内存调优,SparkShuffle调优,调节Executor的堆外内存. 二.具体    1.代码调优 1.避免创建重复的RDD,尽 ...

  8. 【Spark篇】---Spark中内存管理和Shuffle参数调优

    一.前述 Spark内存管理 Spark执行应用程序时,Spark集群会启动Driver和Executor两种JVM进程,Driver负责创建SparkContext上下文,提交任务,task的分发等 ...

  9. 【Spark篇】---Spark中资源和任务调度源码分析与资源配置参数应用

    一.前述 Spark中资源调度是一个非常核心的模块,尤其对于我们提交参数来说,需要具体到某些配置,所以提交配置的参数于源码一一对应,掌握此节对于Spark在任务执行过程中的资源分配会更上一层楼.由于源 ...

随机推荐

  1. 番茄日志发布1.0.3版本-增加Kafka支持

    番茄日志(TomatoLog)能做什么 可能你是第一次听说TomatoLog,没关系,我可以从头告诉你,通过了解番茄日志,希望能帮助有需要的朋友,番茄日志处理将大大降低你采集.分析.处理日志的过程. ...

  2. Spring源码剖析6:Spring AOP概述

    原文出处: 五月的仓颉 我们为什么要使用 AOP 前言 一年半前写了一篇文章Spring3:AOP,是当时学习如何使用Spring AOP的时候写的,比较基础.这篇文章最后的推荐以及回复认为我写的对大 ...

  3. python实例:自动保存百度盘资源到百度盘中

    本实例的实现逻辑是,应用selenium UI自动化登录百度盘,读取存储百度分享地址和提取码的txt文档,打开百度盘分享地址,填入提取码,然后保存到指定的目录中 全部代码如下: # -*-coding ...

  4. Python之函数(一)定义函数以及传参

    定义函数以及传参 函数的定义 def 函数名(): 函数体 例子: def func():#def关键字--定义 func函数名--和变量定义规则一样 ()必须要写格式 :声明 语句结束 s=[1,2 ...

  5. JavaScript 数据结构与算法之美 - 冒泡排序、插入排序、选择排序

    1. 前言 算法为王. 想学好前端,先练好内功,只有内功深厚者,前端之路才会走得更远. 笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 JavaScript ,旨在入门数据结构与算 ...

  6. Python基础学习笔记(一)python发展史与优缺点,岗位与薪资

    相信有好多朋友们都是第一次了解python吧,可能大家也听过或接触过这个编程语言.那么到底什么是python呢?它在什么机缘巧合下诞生的呢?又为什么在短短十几年时间内就流行开来呢?就请大家带着疑问,让 ...

  7. 51nod 1218 最长递增子序列 V2(dp + 思维)

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1218 题解:先要确定这些点是不是属于最长递增序列然后再确定这 ...

  8. Java微服务(一):dubbo-admin控制台的使用

    1.环境准备 使用CentOS7+Docker+Zookeeper3.4.10搭建dubbo微服务 1.1.安装docker容器 (1).uname -r:docker要求CentOS的内核版本高于3 ...

  9. R语言基础入门

    请先安装好R和RStudio 如果不干别的,控制台就是一个内置计算器 2 * 3 #=> 6 sqrt(36) #=> 6, square root log10(100) #=> 2 ...

  10. SpringBoot 参数校验的方法

    Introduction 有参数传递的地方都少不了参数校验.在web开发中,前端的参数校验是为了用户体验,后端的参数校验是为了安全.试想一下,如果在controller层中没有经过任何校验的参数通过s ...