Spark 的基本概念和操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lingzidong/article/details/86555225

Spark 总结

Spark 是流行大数据计算框架之一,和 mapred 相比,这种计算框架更加灵活方便。并且还有强大的机器学习库 Spark Mllib 。

除此之外,我们还可以发现 Spark 项目包含很多组件,这些组件关系密切并且可以相互调用。这样就不需要组合各种工具使用了。

Spark 可以运行在各种集群上,比如 YARN ,同时自带独立集群管理器。

Spark 还支持与 HDFS 进行对接。

import findspark
findspark.init()
import pyspark

spark核心概念

每个Spark程序都由 driver 来负责发起,提交各种任务,Spark 通过 SparkContext 来充当 Spark 应用和集群之间的连接关系。

Spark 的核心操作建立在 RDD(Resilient Distributed Dataset) 也就是弹性分布式数据集上,可以说 Spark 的操作就是建立在 RDD
上的各种操作。而这些操作分为转化操作和核心操作

转化操作是惰性求值的,也就是在调用行动操作之前,转化操作不会被执行。

如果我们的计算过程中需要对一个RDD进行多次运算,那么由于惰性计算,这个RDD会被计算多次。此时我们可以将他进行持久化,将这个RDD的值缓存起来,以便后续使用。

一个简单的Spark例子

首先是 RDD 的创建:
RDD 通过 SparkContext 来创建,提供两种创建方式。
一种是通过sc.parallelize创建,这个也是最简单的办法
如果数据比较大,就没办法一口气保存在内存里了。这个时候我们就需要从文件或者分布式文件系统中读取了。在下面会演示。

from pyspark import SparkContext
sc = SparkContext.getOrCreate()

# 一个求平方数的例子
# 为了防止出现重复创建 Spark Context 可以用上面的方法创建。
nums  = sc.parallelize([1,2,3,4])                 #创建RDD的方法
squred = nums.map(lambda x : x*x).collect()       #对RDD先执行转化操作 map ,之后执行执行操作 collect()
for num in squred :
    print(num)
1
4
9
16

上面的程序中,map操作是一个转化操作,利用参数中的 lambda 表达式来转化输入的 RDD
collect 是常见的行动操作,这个操作将RDD的值全部计算出来。

读取数据

Spark支持很多数据源:

  • 各种文件系统:本地,HDFS,Amazon S3 等等
  • 各种文件格式:文本文件,JSON,CSV,Hadoop 中的 SequenceFile 等等
  • 各种数据库:Spark SQL,Hive,或者传统一些的数据库。

下面我们来举一个读取文本的例子,我们要找到README.md中的包含 ’Python‘ 这个单词的行

lines = sc.textFile("file:///usr/local/spark/README.md")
pythonLines = lines.filter(lambda line : "Python" in line)
print(pythonLines.collect())
['high-level APIs in Scala, Java, Python, and R, and an optimized engine that', '## Interactive Python Shell', 'Alternatively, if you prefer Python, you can use the Python shell:']

如果我们需要读取 HDFS 上的文件,地址就改成hdfs://hostname:port,具体的操作可以参照Spark API Doc

RDD 操作:转化操作

转化操作意思就是对操作对象 RDD 进行转化,返回新的 RDD 。上面的两个例子就说明了转化的含义。

常见的转化操作是 mapfillter 二者都是对 RDD 中的单个元素进行的操作。

map 操作将 RDD 中的每个值按照函数的要求进行计算,返回新的RDD

filter操作将 RDD 中的每个值进行筛选,返回筛选过后的结果

传化操作还分为基本转化操作和伪集合操作

# 基本操作
rdd = sc.parallelize([1,2,3,3])
# map操作,给每个数+1
maprdd = rdd.map(lambda x : x+1)
print("map:",maprdd.collect())
# flatmap,对每个数得到多组输出,返回所有输出的内容
faltmaprdd = rdd.flatMap(lambda x: range(1, x))
print("flaMap:",faltmaprdd.collect())
# filter操作,找到 > 2的数
filterrdd = rdd.filter(lambda x : x > 2)
print("filter:",filterrdd.collect())
# 去重操作
distinctrdd = rdd.distinct()
print("distinct:",distinctrdd.collect())
# 采样操作,第一个参数表示是否替换,返回的结果是不确定的
samplerdd = rdd.sample(False,0.5)
print("sample:",samplerdd.collect())
map: [2, 3, 4, 4]
flaMap: [1, 1, 2, 1, 2]
filter: [3, 3]
distinct: [1, 2, 3]
sample: [2, 3]

flatMap的特性可以用来切分单词

# 切分单词
words = lines.flatMap(lambda line : line.split(" "))
print(words.take(10))
['#', 'Apache', 'Spark', '', 'Spark', 'is', 'a', 'fast', 'and', 'general']

除此之外,还有一类伪集合操作,因为这些集合操作并不是严格意义上的集合操作,具体的可以查阅API

# 简单集合操作
rddA = sc.parallelize([1,2,3])
rddB = sc.parallelize([1,3,4])

# 求并,但是会有重复元素
print(rddA.union(rddB).collect())

# 求交

print(rddA.intersection(rddB).collect())
[1, 2, 3, 1, 3, 4]
[1, 3]

行动操作

行动操作会使得 SPARK 开始计算,比较常见的有获取数据类的collect,take,top
还有聚合数据类的reducefold
由于collect需要把结果放进内存,所以我们可以将使用其他方式来获取数据,collect方式最好用于单元测试

下面是一个reduce的例子

nums = sc.parallelize([1,2,3,4])
sum = nums.reduce(lambda x,y : x + y)
print(sum)
10

向Spark传递函数(以Python为例)

Spark 程序的执行,需要我们自行传递函数到各个环节,除了可以使用 lambda 表达式之外,还可以传递本地定义的函数,或者系统自定义的函数

下面这个例子显示了本地函数的调用,编写本地函数时,一定要注意返回值的类型

传递参数的时候,最好不要传递整个对象,否则的话,Spark 会将这个参数传递到工作节点上去,有的时候会非常的大。
这个时候,我们需要把相对应的成员复制就行

def myFunc(s):
    words = s.split(" ")
    return len(words)
words = lines.map(myFunc)
print(words.take(10))
[3, 1, 14, 13, 11, 12, 8, 6, 1, 1]

猜你喜欢

转载自blog.csdn.net/lingzidong/article/details/86555225