spark编程基础,pyspark

连接

from pyspark import SparkConf, SparkContext
import os

# pyspark使用的python环境地址
os.environ['PYSPARK_PYTHON']='/usr/local/anaconda3/envs/py36_general/bin/python3'


# conf = SparkConf().setMaster("spark://master:7077").setAppName("My App")
conf = SparkConf().setMaster("yarn").setAppName("My App")
sc = SparkContext(conf = conf)

logFile = "file:///usr/local/spark-2.4.7-bin-without-hadoop/README.md"
logData = sc.textFile(logFile, 2).cache()
numAs = logData.filter(lambda line: 'a' in line).count()
numBs = logData.filter(lambda line: 'b' in line).count()
print('Lines with a: %s, Lines with b: %s' % (numAs, numBs))

创建RDD

从本地文件加载

  • 如果在pycharm远程使用此方法的话,必须保证每个节点都在该路径有此文件-----不推荐
  • 但是,直接在命令行交互时不需要所有节点都有
rdd= sc.textFile("file:///home/data/word.txt")
print(rdd.collect())

从hdfs上加载

  • 需要先把文件上传到hdfs里

在HDFS中创建hadoop目录命令:hadoop fs -mkdir /hadoop

上传文件到hadoop目录下:hadoop fs -put 文件名(可以同时上传多个文件) /hadoop

想要递归查看HDFS中的所有文件命令:hadoop fs -ls -R /

查看HDFS中的文件夹的大小:hadoop fs -dus / 或者 hadoop fs -du -s /

从HDFS复制到本地:hadoop fs -get HDFS的文件的位置 要复制到的位置

从HDFS下载多个文件到本地合并成一个文件:hadoop fs -getmerge 文件的位置及名字 文件的位置及名字 本地的文件名

可从master:50070上浏览
在这里插入图片描述

rdd= sc.textFile("hdfs://master:9000/user/centos7-gao/data/word.txt")
print(rdd.collect())

通过并行集合(数组)创建RDD

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

RDD操作

rdd操作分为转换类动作类

对于RDD而言,每一次转换操作都会产生不同的RDD,供给下一个“转换”使用。转换得到的RDD是惰性求值的,也就是说,整个转换过程只是记录了转换的轨迹,并不会发生真正的计算,只有遇到行动操作时,才会发生真正的计算。

下面列出一些常见的转换操作(Transformation API):

  • filter(func):筛选出满足函数func的元素,并返回一个新的数据集
  • map(func):将每个元素传递到函数func中,并将结果返回为一个新的数据集
  • flatMap(func):与map()相似,但每个输入元素都可以映射到0或多个输出结果
  • groupByKey():应用于(K,V)键值对的数据集时,返回一个新的(K, Iterable)形式的数据集
  • reduceByKey(func):应用于(K,V)键值对的数据集时,返回一个新的(K, V)形式的数据集,其中的每个值是将每个key传递到函数func中进行聚合

行动操作是真正触发计算的地方。Spark程序执行到行动操作时,才会执行真正的计算,从文件中加载数据,完成一次又一次转换操作,最终,完成行动操作得到结果。
下面列出一些常见的行动操作(Action API):

  • count() 返回数据集中的元素个数
  • collect() 以数组的形式返回数据集中的所有元素
  • first() 返回数据集中的第一个元素
  • take(n) 以数组的形式返回数据集中的前n个元素
  • reduce(func) 通过函数func(输入两个参数并返回一个值)聚合数据集中的元素
  • foreach(func) 将数据集中的每个元素传递到函数func中运行

栗子

map&filter

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

rdd = rdd.filter(lambda x: x>2)
print(rdd.collect())

rdd = rdd.map(lambda x: x+2)
print(rdd.collect())

在这里插入图片描述

map&flatMap对比

array = ["hadoop is good", "spark is fast", "spark is better"]
rdd = sc.parallelize(array)
print(rdd.collect())

rdd = rdd.map(lambda line: line.split(" "))
print(rdd.collect())

array = ["hadoop is good", "spark is fast", "spark is better"]
rdd = sc.parallelize(array)
print(rdd.collect())

rdd = rdd.flatMap(lambda line: line.split(" "))
print(rdd.collect())

在这里插入图片描述
关于键值对的相关操作
常用的键值对转换操作包括reduceByKey()、groupByKey()、sortByKey()、join()、cogroup()等

reduceByKey()顾名思义:按键分组操作
groupByKey(): 按键分组,生成的值为迭代器

list = ["Hadoop","Spark","Hive","Spark"]
rdd = sc.parallelize(list)
pairRDD = rdd.map(lambda word : (word,1))
print(pairRDD.collect())

reduceByKey = pairRDD.reduceByKey(lambda a,b: a+b)
print(reduceByKey.collect())

groupByKey = pairRDD.groupByKey()
print(groupByKey.collect())

在这里插入图片描述
其他转换见 http://dblab.xmu.edu.cn/blog/1706-2/

持久化

在Spark中,RDD采用惰性求值的机制,每次遇到行动操作,都会从头开始执行计算。如果整个Spark程序中只有一次行动操作,这当然不会有什么问题。但是,在一些情形下,我们需要多次调用不同的行动操作,这就意味着,每次调用行动操作,都会触发一次从头开始的计算。这对于迭代计算而言,代价是很大的,迭代计算经常需要多次重复使用同一组数据。

实际上,可以通过持久化(缓存)机制避免这种重复计算的开销。可以使用persist()方法对一个RDD标记为持久化,之所以说“标记为持久化”,是因为出现persist()语句的地方,并不会马上计算生成RDD并把它持久化,而是要等到遇到第一个行动操作触发真正计算以后,才会把计算结果进行持久化,持久化后的RDD将会被保留在计算节点的内存中被后面的行动操作重复使用。
persist()的圆括号中包含的是
持久化级别
参数,比如,persist(MEMORY_ONLY)表示将RDD作为反序列化的对象存储于JVM中,如果内存不足,就要按照LRU原则替换缓存中的内容。persist(MEMORY_AND_DISK)表示将RDD作为反序列化的对象存储在JVM中,如果内存不足,超出的分区将会被存放在硬盘上。一般而言,使用cache()方法时,会调用persist(MEMORY_ONLY)。

栗子
注意:第二次操作没有重新计算,而是直接从缓存中获取

list = ["Hadoop","Spark","Hive"]
rdd = sc.parallelize(list)

# 会调用persist(MEMORY_ONLY),但是,语句执行到这里,并不会缓存rdd,这是rdd还没有被计算生成
rdd.cache()
# 第一次行动操作,触发一次真正从头到尾的计算,这时才会执行上面的rdd.cache(),把这个rdd放到缓存中
print(rdd.count())
# 第二次行动操作,不需要触发从头到尾的计算,只需要重复使用上面缓存中的rdd
print(','.join(rdd.collect()))

分区

RDD是弹性分布式数据集,通常RDD很大,会被分成很多个分区,分别保存在不同的节点上。RDD分区的一个分区原则是使得分区的个数尽量等于集群中的CPU核心(core)数目。

分区的好处:

  1. 使rdd分布在不同的机器上,可以同时使用不同机的算力
  2. 可减少网络通讯开销

对于不同的Spark部署模式而言(本地模式、Standalone模式、YARN模式、Mesos模式),都可以通过设置spark.default.parallelism这个参数的值,来配置默认的分区数目,一般而言:

  • 本地模式:默认为本地机器的CPU数目,若设置了local[N],则默认为N;
  • Apache Mesos:默认的分区数为8;
  • Standalone或YARN:在“集群中所有CPU核心数目总和”和“2”二者中取较大值作为默认值;

因此,对于parallelize而言,如果没有在方法中指定分区数,则默认为spark.default.parallelism,比如:

array = [1,2,3,4,5]
rdd = sc.parallelize(array,2) #设置两个分区

对于textFile而言,如果没有在方法中指定分区数,则默认为min(defaultParallelism,2),其中,defaultParallelism对应的就是spark.default.parallelism。
如果是从HDFS中读取文件,则分区数为文件分片数(比如,128MB/片)。

手动设置分区

# 手动设置分区
data = sc.parallelize([1, 2, 3, 4], 2)
# 打印分区
print(len(data.glom().collect()))
# 重新设置分区
rdd = data.repartition(1)
print(len(rdd.glom().collect()))

自定义分区
在这里插入图片描述

在这里插入图片描述

共享变量

广播变量

广播变量(broadcast variables)允许程序开发人员在每个机器上缓存一个只读的变量,而不是为机器上的每个任务都生成一个副本。通过这种方式,就可以非常高效地给每个节点(机器)提供一个大的输入数据集的副本。Spark的“动作”操作会跨越多个阶段(stage),对于每个阶段内的所有任务所需要的公共数据,Spark都会自动进行广播。通过广播方式进行传播的变量,会经过序列化,然后在被任务使用时再进行反序列化。这就意味着,显式地创建广播变量只有在下面的情形中是有用的:当跨越多个阶段的那些任务需要相同的数据,或者当以反序列化方式对数据进行缓存是非常重要的。

可以通过调用SparkContext.broadcast(v)来从一个普通变量v中创建一个广播变量。这个广播变量就是对普通变量v的一个包装器,通过调用value方法就可以获得这个广播变量的值,具体代码如下:

v = [1, 2, 3]
broadcastVar = sc.broadcast(v)
print(broadcastVar.value)

这个广播变量被创建以后,那么在集群中的任何函数中,都应该使用广播变量broadcastVar的值,而不是使用v的值,这样就不会把v重复分发到这些节点上。此外,一旦广播变量创建后,普通变量v的值就不能再发生修改,从而确保所有节点都获得这个广播变量的相同的值。

累加器

累加器是仅仅被相关操作累加的变量,通常可以被用来实现计数器(counter)和求和(sum)。Spark原生地支持数值型(numeric)的累加器,程序开发人员可以编写对新类型的支持。如果创建累加器时指定了名字,则可以在Spark UI界面看到,这有利于理解每个执行阶段的进程。
一个数值型的累加器,可以通过调用SparkContext.accumulator()来创建。运行在集群中的任务,就可以使用add方法来把数值累加到累加器上,但是,这些任务只能做累加操作,不能读取累加器的值,只有任务控制节点(Driver Program)可以使用value方法来读取累加器的值。
下面是一个代码实例,演示了使用累加器来对一个数组中的元素进行求和:

accum = sc.accumulator(0)
rdd = sc.parallelize([1, 2, 3, 4]).foreach(lambda x: accum.add(x))
print(accum.value)
# 10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值