Spark-RDD Programming Guide官网二

Overview

Spark中的第二个抽象是可用于并行操作的共享变量。默认情况下,当Spark作为不同节点上的一组任务并行运行函数时,它将函数中使用的每个变量的副本发送给每个任务。有时候,需要在任务之间或任务和驱动程序之间共享变量。Spark支持两种类型的共享变量:广播变量(可以用于在所有节点的内存中缓存值)和累加器(仅“添加”到其中的变量,如计数器和和)。

Linking with Spark

在Spark 2.2.0中删除了Python 2.6的支持
首先导入包

from pyspark import SparkContext, SparkConf

PySpark在驱动程序和工作程序中都需要相同的Python小版本。它在PATH中使用了默认的python版本,您可以指定PYSPARK_PYTHON想要使用的python版本,例如:

 PYSPARK_PYTHON=python3.4 bin/pyspark
 PYSPARK_PYTHON=/opt/pypy-2.5/bin/pypy bin/spark-submit examples/src/main/python/pi.py

Initializing Spark

Spark程序必须做的第一件事是创建SparkContext对象,该对象告诉Spark如何访问集群。要创建SparkContext,首先需要构建一个包含应用程序信息的SparkConf对象。

conf = SparkConf().setAppName(appName).setMaster(master)
sc = SparkContext(conf=conf)

appName参数是应用程序在集群UI上显示的名称。master是一个Spark、Mesos或纱线集群URL,或者是在本地模式下运行的特殊“本地”字符串。实际上,在集群上运行时,您不希望在程序中硬编码master,而是使用spark-submit在那里启动应用程序并接收它。但是,对于本地测试和单元测试,您可以通过“本地”运行Spark进程中。

Using the Shell

还可以在增强的Python解释器IPython中启动PySpark shell。PySpark与IPython 1.0.0及更高版本一起工作。要使用IPython,在运行bin/pyspark时将PYSPARK_DRIVER_PYTHON变量设置为IPython:

$ PYSPARK_DRIVER_PYTHON=ipython ./bin/pyspark

使用Jupyter笔记本(以前称为IPython笔记本)

$ PYSPARK_DRIVER_PYTHON=jupyter PYSPARK_DRIVER_PYTHON_OPTS=notebook ./bin/pyspark

Resilient Distributed Datasets (RDDs)

创建RDDs有两种方法:在驱动程序中并行化现有集合,或在外部存储系统中引用数据集,例如共享文件系统、HDFS、HBase或任何提供Hadoop InputForma的数据源

Parallelized Collections

创建RDD方式一

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

我们可以调用distData。reduce(lambda a, b: a + b)以将列表中的元素相加。稍后我们将描述对分布式数据集的操作。

External Datasets

创建RDD方式二

distFile = sc.textFile("data.txt")

注意坑

  1. 如果使用本地文件系统上的路径,则必须在工作节点上的同一路径上访问该文件。要么将文件复制到所有工作者,要么使用一个挂载网络的共享文件系统。我是直接把代码和数据上传到服务器,让集群自动把代码分配下去
  2. Spark所有基于文件的输入方法,包括文本文件,支持在目录、压缩文件和通配符上运行。例如,可以使用textFile(“/my/directory”)、textFile(“/my/directory/.txt”)和textFile(“/my/directory/.gz”)。
  3. textFile方法还接受一个可选的第二个参数,用于控制文件的分区数量。默认情况下,Spark为文件的每个块创建一个分区(HDFS中的块默认为128MB),但是您也可以通过传递一个较大的值来请求更多的分区。注意,分区不能少于块。即sc.textFile(filepath,n) n表示分区个数
  4. SparkContext。wholeTextFiles允许您读取包含多个小文本文件的目录,并将它们作为(文件名、内容)对返回。这与textFile相反,textFile将在每个文件中每行返回一条记录。
  5. Spark SQL是首选的方法。推荐

RDD Operations

map是一个转换,它通过一个函数传递每个数据集元素,并返回一个表示结果的新RDD。另一方面,reduce是一个使用某种函数聚合RDD的所有元素并将最终结果返回给驱动程序的操作(尽管还有一个返回分布式数据集的并行reduceByKey)

Basics

lines = sc.textFile("data.txt")
lineLengths = lines.map(lambda s: len(s))
totalLength = lineLengths.reduce(lambda a, b: a + b)

第一行从外部文件定义了一个基本的RDD。此数据集不加载到内存中或以其他方式作用于:行只是指向文件的指针。第二行将linelength定义为映射转换的结果。同样,由于懒惰,linelength不会立即计算。最后,我们运行reduce,这是一个动作。在这一点上,Spark将计算分解为在单独的机器上运行的任务,每台机器都运行它的map部分和局部约简,只返回它的答案到驱动程序。与我在课上听的略有不同

lineLengths.persist()持久化

Passing Functions to Spark(python没怎么学,需要去官网看url

Spark API严重依赖于将驱动程序中的函数传递到集群上运行。有三种推荐的方法:

  1. Lambda表达式,用于可以写成表达式的简单函数。(Lambdas不支持多语句函数或不返回值的语句。)推荐
  2. 调用Spark函数内部的本地defs,用于更长的代码。
  3. 模块中的顶级函数。

Understanding closures(理解闭包)

Spark的难点之一是理解跨集群执行代码时变量和方法的范围和生命周期。在作用域之外修改变量的RDD操作经常会引起混淆。在下面的示例中,我们将查看使用foreach()来增加计数器的代码,但其他操作也可能出现类似的问题。

Example

考虑下面简单的RDD元素和,它的行为可能不同,这取决于是否在同一个JVM中执行。一个常见的例子是在本地模式下运行Spark (- master = local[n]),而不是将Spark应用程序部署到集群(例如通过Spark -submit to YARN):

counter = 0
rdd = sc.parallelize(data)

# Wrong: Don't do this!!
def increment_counter(x):
    global counter
    counter += x
rdd.foreach(increment_counter)

print("Counter value: ", counter)

Local vs. cluster modes

集群模式下:
发送到每个执行程序的闭包中的变量(这里指counter)现在都是副本,因此,在foreach函数中引用计数器时,它不再是驱动节点上的计数器。驱动节点的内存中仍然有一个计数器,但是执行程序不再可见!执行器只看到序列化闭包中的副本。因此,计数器的最终值仍然为零,因为计数器上的所有操作都引用了序列化闭包中的值。
在本地模式下:
在本地模式中,在某些情况下,foreach函数将实际在与驱动程序相同的JVM中执行,并引用相同的原始计数器,并可能实际更新它。
总结:
一般来说,闭包——像循环或局部定义的方法这样的结构,不应该用来改变某些全局状态。Spark不定义或保证从闭包外部引用对象的突变行为。一些这样做的代码可能在本地模式下工作,但这只是偶然的,这样的代码在分布式模式下不会像预期的那样工作。如果需要一些全局聚合,可以使用累加器。
推荐使用累加器。Spark中的累加器专门用于在集群中的工作节点之间分割执行时安全地更新变量。

Printing elements of an RDD

另一个常见的习惯用法是尝试使用RDD .foreach(println)或RDD .map(println)打印RDD的元素。在一台机器上,这将生成预期的输出并打印所有RDD元素。但是,在集群模式下,执行程序(指worker结点执行代码)调用的stdout的输出现在写到了执行程序的stdout,而不是驱动程序(指master提交运行代码)上的,所以驱动程序上的stdout不会显示这些!要打印驱动程序上的所有元素,可以使用collect()方法首先将RDD带到驱动节点,例如:RDD .collect().foreach(println)。但是,这可能导致驱动程序内存不足,因为collect()将整个RDD提取到一台机器上;如果只需要打印RDD的一些元素,更安全的方法是使用take(): RDD .take(100).foreach(println)。

Working with Key-Value Pairs


lines = sc.textFile("data.txt")
pairs = lines.map(lambda s: (s, 1))
counts = pairs.reduceByKey(lambda a, b: a + b)

例如,我们还可以使用count . sortByKey按字母顺序对对进行排序,最后使用count .collect()将它们作为对象列表带回驱动程序。

Transformations

map(func)
filter(func)
flatMap(func) 解释一先执行map然后拍合成一个大的集合
解释二map(func)函数会对每一条输入进行指定的func操作,然后为每一条输入返回一个对象;而flatMap(func)也会对每一条输入进行执行的func操作,然后每一条输入返回一个相对,但是最后会将所有的对象再合成为一个对象;从返回的结果的数量上来讲,map返回的数据对象的个数和原来的输入数据是相同的,而flatMap返回的个数则是不同的。
解释三在使用时map会将一个长度为N的RDD转换为另一个长度为N的RDD;而flatMap会将一个长度为N的RDD转换成一个N个元素的集合,然后再把这N个元素合成到一个单个RDD的结果集。
概括:就是map后的每个小行整合成一个大的行

mapPartitions(func)
与map类似,但是在RDD的每个分区(块)上分别运行,所以func必须是类型Iterator => Iterator,而在类型为T的RDD上运行。
mapPartitionsWithIndex(func)
与mapPartitions类似,但还为func提供一个整数值,表示分区的索引,因此func必须是类型(Int, Iterator) =>迭代器,在类型为T的RDD上运行。
sample(withReplacement, fraction, seed)
使用给定的随机数生成器种子,对数据的一部分进行采样,无论是否进行替换。
union(otherDataset)将两个RDD追加在一起
intersection(otherDataset)
返回一个新的RDD,其中包含源数据集中元素和参数的交集。
distinct([numPartitions]))
groupByKey([numPartitions])
reduceByKey(func, [numPartitions])
aggregateByKey(zeroValue)(seqOp, combOp, [numPartitions])
sortByKey([ascending], [numPartitions])
join(otherDataset, [numPartitions])
cogroup(otherDataset, [numPartitions])
cartesian(otherDataset)
pipe(command, [envVars])
coalesce(numPartitions)
repartition(numPartitions)
repartitionAndSortWithinPartitions(partitioner)
注意:打斜杠的方法我没用过

Actions

reduce(func)
collect()
count()
first()
take(n)
takeSample(withReplacement, num, [seed])
takeOrdered(n, [ordering])
saveAsTextFile(path)
saveAsSequenceFile(path) (Java and Scala)
saveAsObjectFile(path) (Java and Scala)
countByKey()只在类型为(K, V)的RDDs上可用。返回一个(K, Int)对的hashmap和每个键的计数
foreach(func)

Performance Impact

Shuffle是一种昂贵的操作,因为它涉及到磁盘I/O、数据序列化和网络I/O。要组织shuffle的数据,Spark生成一组任务——映射任务来组织数据,以及一组reduce任务来聚合数据。这个术语来自MapReduce,与Spark的map和reduce操作没有直接关系。
在内部,单个map任务的结果会保存在内存中,直到无法匹配为止。然后,根据目标分区对它们进行排序,并将它们写入单个文件。在reduce方面,任务读取相关的排序块。
某些shuffle操作可能会消耗大量的堆内存,因为它们使用内存中的数据结构来组织在传输记录之前或之后的记录。具体来说,reduceByKey和aggregateByKey在map端创建这些结构,ByKey操作在reduce端生成这些结构。当数据不适合内存时,Spark会将这些表泄漏到磁盘上,从而增加磁盘I/O的额外开销,并增加垃圾收集。
Shuffle还会在磁盘上生成大量的中间文件。在Spark 1.3中,这些文件被保存,直到相应的RDDs不再使用并被垃圾收集。这样做是为了在重新计算沿袭时不需要重新创建shuffle文件。如果应用程序保留对这些RDDs的引用,或者GC不频繁启动,那么垃圾收集可能在很长一段时间之后才会发生。这意味着长时间运行的Spark作业可能会消耗大量磁盘空间。临时存储目录由spark.local指定。dir配置pa
可以通过调整各种配置参数来调整Shuffle行为。参见Spark配置指南中的“Shuffle行为”部分。

RDD Persistence

Spark最重要的功能之一是跨操作在内存中持久化(或缓存)数据集。当您持久化一个RDD时,每个节点将它在内存中计算的任何分区存储起来,并在该数据集(或从该数据集派生的数据集)上的其他操作中重用它们。这使得未来的动作更快(通常超过10倍)。缓存是迭代算法和快速交互使用的关键工具
您可以使用persist()或cache()方法将RDD标记为持久化。第一次在操作中计算它时,它将保存在节点上的内存中。Spark缓存是容错的——如果RDD的任何分区丢失,它将使用最初创建它的转换自动重新计算。
此外,每个持久化的RDD都可以使用不同的存储级别进行存储,例如,允许您在磁盘上持久化数据集,在内存中持久化数据集,但是作为序列化的Java对象(以节省空间),跨节点复制数据集。这些级别是通过传递一个StorageLevel对象(Scala、Java、Python)来持久()来设置的。cache()方法是使用默认存储级别StorageLevel的简写方式。MEMORY_ONLY(在内存中存储反序列化的对象)。全部存储级别为:

  1. MEMORY_ONLY
  2. MEMORY_AND_DISK
  3. MEMORY_ONLY_SER (Java and Scala)
  4. MEMORY_AND_DISK_SER (Java and Scala)
  5. DISK_ONLY
  6. MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc.
  7. OFF_HEAP (experimental)
    注意:
    在Python中,存储的对象总是使用Pickle库进行序列化,因此选择序列化级别并不重要。Python中可用的存储级别包括MEMORY_ONLY、MEMORY_ONLY_2、MEMORY_AND_DISK、MEMORY_AND_DISK_2、DISK_ONLY和DISK_ONLY_2。
    Spark还会在shuffle操作中自动持久化一些中间数据(例如reduceByKey),甚至在没有用户调用persist的情况下也是如此。这样做是为了避免在转移期间节点失败时重新计算整个输入。如果用户打算重用生成的RDD,我们仍然建议他们调用持久化。

Removing Data

Spark自动监视每个节点上的缓存使用情况,并以最近最少使用的方式删除旧数据分区。如果您想手动删除一个RDD,而不是等待它从缓存中退出,请使用RDD.unpersist()方法。

Shared Variables

通常,当传递给Spark操作(如map或reduce)的函数在远程集群节点上执行时,它在函数中使用的所有变量的单独副本上工作。这些变量被复制到每台机器上,对远程机器上的变量的更新不会传播回驱动程序。跨任务支持通用的读写共享变量将是低效的。但是,Spark确实为两种常见的使用模式提供了两种有限类型的共享变量:广播变量和累加器。
Broadcast Variables:
广播变量允许程序员将只读变量缓存在每台机器上,而不是将它的副本与任务一起发送出去。例如,可以使用它们以高效的方式为每个节点提供一个大型输入数据集的副本。Spark还尝试使用高效的广播算法来分配广播变量,以降低通信成本。
Spark操作通过一组阶段执行,由分布式“shuffle”操作分隔。Spark自动广播任务在每个阶段所需的公共数据。以这种方式广播的数据在运行每个任务之前以序列化的形式缓存和反序列化。这意味着,只有当跨多个阶段的任务需要相同的数据或以反序列化的形式缓存数据时,显式地创建广播变量才有用。
Broadcast变量是通过调用SparkContext.broadcast(v)从变量v中创建的。broadcast变量是v的包装器,它的值可以通过调用value方法来访问。下面的代码显示如下:

>>> broadcastVar = sc.broadcast([1, 2, 3])
<pyspark.broadcast.Broadcast object at 0x102789f10>

>>> broadcastVar.value
[1, 2, 3]

在创建广播变量之后,应该在集群上运行的任何函数中使用它而不是值v,这样v就不会被多次发送到节点。此外,对象v在广播后不应该进行修改,以确保所有节点都能获得广播变量的相同值(例如,如果稍后将该变量发送到新节点)。
Accumulators:
累加器是仅通过关联和交换操作“添加”的变量,因此可以有效地并行支持。它们可以用于实现计数器(如MapReduce)或和。Spark本机支持数字类型的累加器,程序员可以添加对新类型的支持。
作为用户,您可以创建命名的或未命名的累加器。如下图所示,一个命名累加器(在这个实例计数器中)将显示在修改该累加器的阶段的web UI中。Spark显示“Tasks”表中由任务修改的每个累加器的值。

通过调用SparkContext.accumulator(v)从一个初始值v创建累加器。然后,在集群上运行的任务可以使用add方法或+=操作符添加到集群中。然而,他们无法读懂它的价值。只有驱动程序可以使用累加器的value方法读取累加器的值。
下面的代码显示了一个累加器,用于将数组的元素相加:

>>> accum = sc.accumulator(0)
>>> accum
Accumulator<id=0, value=0>

>>> sc.parallelize([1, 2, 3, 4]).foreach(lambda x: accum.add(x))
...
10/09/29 18:41:08 INFO SparkContext: Tasks finished in 0.317106 s

>>> accum.value
10
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值