总览

第一、每个spark 应用都有一个驱动程序去运行着主函数和再每个节点上的并行操作。

spark提供了一个RDD(弹性分布式数据集)的数据集合,可以通过不同的节点并行操作运算,可以通过hdfs文件构建。RDD可以在内存中进行缓存,当需要复用的时候会有更高的效率。

第二、提供了共享变量(shared varibales)在不同节点的并行操作中使用。一个是广播变量(broadcast variables)一个是累加器(accumulators)。当一个变量需要在不同节点任务重共享或者节点任务跟驱动程序见有变量共享需求时,可以用这俩工具。

连接spark

from pyspark import SparkContext, SparkConf
  • SparkContext: 用来连接集群节点的一个接口
  • SparkConf:
  1. spark应用的配置,将各种spark参数设置为key-value对。程序启动时会从系统中自动读取各种配置参数,若SparkConf有配置则以SparkConf优先。
  2. 设置SparkConf(false),可以避免加在外部系统设置,完全以SparkConf为准。
  3. 所有的配置设置支持链式写法,比如:

SparkConf().setMaster(“local”).setAppName(“My app”)

初始化spark

from pyspark import SparkContext, SparkConf

conf = SparkConf().setAppName('programm_guide')
sc = SparkContext(conf=conf)
print ('the sc is: {}'.format(sc))

使用shell

将spark bin 目录加入环境变量,直接运行

pyspark

RDD

Parallelized Collections

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

使用 sc.parallelize 接口可以从一个迭代器或者集合中创建RDD。迭代器或者集合中的数据会被copy并创建为可以并行计算的弹性分布式数据集,即RDD。

通常RDD数据会被分区,可以指定参数说明数据要被划分为多少个分区。比如:

sc.parallelize(data, 10))

外部数据集

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

spark支持从不限于以下途径读取数据创建RDD:

  1. HDFS
  2. Cassandra
  3. HBase
  4. Amazon S3
  5. local

spark支持直接读取 text files, SequenceFiles以及其他hadoop输入格式。

使用spark读取文件需要注意以下几点:

  1. 若读取路径为本地路径,需要保证节点上的相同路径必须是存在且可访问的。
  2. 所有spark读取文件操作比如sc.textFile 全都支持对文件夹、压缩文件及通配符文件名的支持。比如:
rdd = sc.textFile("/my/directory")
rdd = sc.textFile("/my/directory/*.txt")
rdd = sc.textFile("/my/directory/*.gz")

其他:

  1. SparkContext.wholeTextFiles

    支持从一个文件夹中读取很多小文件,按照(filename, content)的pairs返回RDD。相反,sc.textFile的方法读取文件夹会返回一个RDD, 每个文件的每一行都是一条 RDD record。。
  2. RDD.saveAsPickleFileSparkContext.pickleFile

    用于将RDD序列化为python对象的格式。
  3. 保存和加载 SequenceFiles
rdd = sc.parallelize(range(1, 4)).map(lambda x: (x, "a" * x))
rdd.saveAsSequenceFile("path/to/file")
sorted(sc.sequenceFile("path/to/file").collect())
  1. 保存和加载其他hadoop 输入输出格式的数据:...

RDD操作

RDD支持两类操作:

1. transformations

所有的transformations都是惰性(lazy)执行的,比如map。简单来说就是所有的transformations都不是立即执行的,程序知会记下来它对data都进行了哪些transformations以及相应的执行顺序,只有当他碰到一个action的时候才会真正进行计算并返回结果给驱动程序。lazy执行的设计可以让spark程序的运行更有效率。

每个transform后的的RDD都可以通过rdd.persist() 或者 rdd.cache() 的方法进行缓存再利用。

2. actions

跟transformation相反,立即执行的。

基础

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

读取数据,maop 并 持久化,reduce 操作。

将函数传递给spark

三种方式:

  • lambda 表达式
rdd.map(lambda s: len(s))
  • def 定义的函数
def myFunc(s):
words = s.split(" ")
return len(words) sc = SparkContext(...)
sc.textFile("file.txt").map(myFunc)

由于节点访问外部对象的属性将会导致引用整个对象,最好在函数内通过复制一份局部变量来进行操作。

class MyClass(object):
def __init__(self):
self.field = "Hello"
def doStuff(self, rdd):
field = self.field
return rdd.map(lambda s: field + s)
  • spark 本身封装的一些高级模块函数

理解闭包

理解spark的难点之一是跨节点执行代码时准确理解variables和methods的适用范围与生命周期。修改超出范围的变量的RDD操作可能会引起混乱。下面通过foreach()的案例来看一下。

案例

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)

这段代码用于计算rdd中record的条目数。注意这种写法是错的!

本地模式 vs 节点模式

上面的写法并不对。spark执行程序会把rdd操作分解为任务,每个任务由执行程序运行。执行任务前,spark会计算任务的closure,也就是执行程序进行计算时所必须可见的那些variables 和 methods,并发送给每个executor。

在上面代码中,counter变量已经copy并发送给每个节点的执行程序中,相当于不同节点执行程序的局部变量,每个节点都是对局部变量进行更改。最终驱动程序的counter仍然为0。

当local模式运行程序,某些时候foreach函数将在与驱动程序相同的JVM中执行,并引用全局的counter,这时候程序会有效。但这种写法仍然不是推荐的写法。

为解决上述场景中的问题,spark提供了累加器(Accumulator),后面会有详细用法介绍。

打印RDD元素

rdd.collect()获得rdd中的value,然后再进行打印。但这不是一个好的方式。因为collect操作会把rdd中所有数据汇总到一台机器上面,当rdd中的数据很多时会out of memory。

rdd.foreach(println)的方式也不行,标准输出会输出到不同节点上去。

正确的做法应该是rdd.take(100).foreach(println)

处理 key-value 数据对

大部分RDD的操作都支持任意类型的数据,但有些操作只支持key-value类型的数据。最常见的是分布式 shuffle 操作,比如group 和 aggregating。

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

transformations

详见:RDD接口文档

actions

详见:RDD接口文档

Spark RDD API 支持异步操作,比如针对foreachforeachAsync

shuffle 操作

shuffle 是重新分配数据的机制,以便跨分区对数据进行不同分组。涉及跨执行程序和机器复制数据,是复杂且昂贵的操作。

背景

以reduceByKey操作为例。

单个key的所有value可能不在同一个分区甚至同一台机器上,但又必须把同个key的value一起计算出结果。

spark在计算期间,单个task在单个分区上操作,那为了组织同个key的所有的value,spark必须读取所有分区的所有数据将相同key的value汇总在一起,得出最终结果。这就是shuffle。

会导致shuffle的操作包括repartition 操作比如 repartition 和 coalesce;ByKey操作比如groupByKey 和 reduceByKey,join操作比如 cogroup 和 join。

性能影响

shuffle涉及磁盘IO,数据序列化,网络IO,比较昂贵的操作。

reduceByKey 和 aggregateByKey操作会格外消耗栈内存,这些操作涉及到的数据转存的操作都是要内存数据结构来组织。当数据不适合内存操作时会产生额外的磁盘IO的开销以及垃圾回收。

shuffle会产生大量中间文件,spark会暂时保留这些文件知道不再使用相应的RDD了才会进行垃圾回收。如果程序保留的对相应RDD的引用或者垃圾回收不经常启动,那么比较长时间的spark任务会占用大量的磁盘空间。

RDD 持久化

RDD需要在程序中重复使用时使用RDD的持久化技术可以加速程序运行。

rdd.persist()
rdd.cache()

持久化有不同的storage level可以选择

其中 MEMORY_ONLY 是 rdd.cache() 的default选项。

storage level 的选择

storage level 的选择就是在内存使用和cpu效率间的取舍。建议按照以下几个原则进行选择:

  1. 默认的MEMORY_ONLY是CPU高效率的选择,如果程序运行良好,尽量不要改。
  2. 若程序运行性能不佳,尝试 MEMORY_ONLY_SER(仅适用于JAVA/SCALA)
  3. 如果要快速修复故障,使用基于复制的storage level。在程序运行期间会有较好的容错性,丢失的数据会自动从备份中找回来继续计算。

remove data

spark会监控每个节点的内存使用情况,并根据LRU(least-recently-used)原则进行垃圾回收。也可以通过RDD.unpersist()的方式手动remove一个RDD。

共享变量

广播变量

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

sc.broadcast 创立的共享变量可以传递给每个节点调用。他一旦传递出去后,不应该再被驱动程序更改。每个节点的执行程序都获得一样的value。

累加器(Accumulators)

两种方式

  1. sc.accumulator
accum = sc.accumulator(0)
sc.parallelize([1, 2, 3, 4]).foreach(lambda x: accum.add(x))
accum.value

sc.accumulator(0) 给 accum 一个初始化的值,accum 可以被所有节点使用add方法进行操作,但不能读取其中的value。只有驱动程序才能通过 accm.value 读取其value。

  1. 继承 AccumulatorParam 类,这玩意儿具体用法还有待研究。
class VectorAccumulatorParam(AccumulatorParam):
def zero(self, initialValue):
return Vector.zeros(initialValue.size) def addInPlace(self, v1, v2):
v1 += v2
return v1 # Then, create an Accumulator of this type:
vecAccum = sc.accumulator(Vector(...), VectorAccumulatorParam())

部署

详见:官方文档

spark 一、编程指南的更多相关文章

  1. Spark Graphx编程指南

    问题导读1.GraphX提供了几种方式从RDD或者磁盘上的顶点和边集合构造图?2.PageRank算法在图中发挥什么作用?3.三角形计数算法的作用是什么?Spark中文手册-编程指南Spark之一个快 ...

  2. Apache Spark 2.2.0 中文文档 - Spark Streaming 编程指南 | ApacheCN

    Spark Streaming 编程指南 概述 一个入门示例 基础概念 依赖 初始化 StreamingContext Discretized Streams (DStreams)(离散化流) Inp ...

  3. <译>Spark Sreaming 编程指南

    Spark Streaming 编程指南 Overview A Quick Example Basic Concepts Linking Initializing StreamingContext D ...

  4. Apache Spark 2.2.0 中文文档 - Spark Streaming 编程指南

    Spark Streaming 编程指南 概述 一个入门示例 基础概念 依赖 初始化 StreamingContext Discretized Streams (DStreams)(离散化流) Inp ...

  5. Spark—GraphX编程指南

    Spark系列面试题 Spark面试题(一) Spark面试题(二) Spark面试题(三) Spark面试题(四) Spark面试题(五)--数据倾斜调优 Spark面试题(六)--Spark资源调 ...

  6. Spark Streaming编程指南

    Overview A Quick Example Basic Concepts Linking Initializing StreamingContext Discretized Streams (D ...

  7. Spark SQL编程指南(Python)

    前言   Spark SQL允许我们在Spark环境中使用SQL或者Hive SQL执行关系型查询.它的核心是一个特殊类型的Spark RDD:SchemaRDD.   SchemaRDD类似于传统关 ...

  8. Spark SQL编程指南(Python)【转】

    转自:http://www.cnblogs.com/yurunmiao/p/4685310.html 前言   Spark SQL允许我们在Spark环境中使用SQL或者Hive SQL执行关系型查询 ...

  9. Spark官方3 ---------Spark Streaming编程指南(1.5.0)

    Design Patterns for using foreachRDD dstream.foreachRDD是一个强大的原语,允许将数据发送到外部系统.然而,了解如何正确有效地使用该原语很重要.避免 ...

  10. Spark(1.6.1) Sql 编程指南+实战案例分析

    首先看看从官网学习后总结的一个思维导图 概述(Overview) Spark SQL是Spark的一个模块,用于结构化数据处理.它提供了一个编程的抽象被称为DataFrames,也可以作为分布式SQL ...

随机推荐

  1. kubernets之服务重定向

    一  服务的强大功能之处的其他表现 前面介绍的所有有关服务的说明,都是将集群内部的pod应用暴露出来提供外部客户端或者内部的客户端进行访问,但是服务的强大之处远远不止于此 服务甚至可以将集群外部的应用 ...

  2. apijson简单使用

    apijson简单使用 介绍 APIJSON 是一种专为 API 而生的 JSON 网络传输协议 以及 基于这套协议实现的 ORM 库.为简单的增删改查.复杂的查询.简单的事务操作 提供了完全自动化的 ...

  3. python7、8章

    目录 第七章 用户输入和while循环 7.1 函数input()的工作原理 7.1.1 编写清晰的程序 7.1.2 使用int()来获取数值输入 分析: 结果: 7.1.3 求模运算符 7.1.4 ...

  4. 入门OJ:八中生成树2

    题目描述 八中里面有N个建设物,M条边.对于这种要建最小生成树的问题,你应该很熟练了.现在老大决定降低某条边的费用,然后这条边必须要被选中,因为这条路他每天都要走,自然......问选了这条边后是否可 ...

  5. 干电池升压IC

    1, 干电池升压IC                         升压输出3V,3,3V,5V等3V-5V可调   2, 单节锂电池升压IC                     升压输出4.2 ...

  6. Flask中的g到底是个什么鬼?

    g到底是个什么鬼? 在一次请求请求的周期,可以在g中设置值,在本次的请求周期中都可以读取或复制. 相当于是一次请求周期的全局变量. from flask import Flask,g app = Fl ...

  7. ETCD数据迁移

    ETCD数据迁移 本文阅读对象为想要将Rainbond平台rbd-etcd切换至外部etcd的相关人员. 在k8s master节点创建secret 本文中将要切换的ETCD为根据Rainbond官方 ...

  8. linux设备注册

    一.分配cdev cdev表示字符设备,使用cdev_alloc函数,cdev_alloc函数原型如下: /** * cdev_alloc() - allocate a cdev structure ...

  9. Seata RPC 模块的重构之路

    简介: RPC 模块是我最初研究 Seata 源码开始的地方,因此我对 Seata 的 RPC 模块有过一些深刻研究,在我研究了一番后,发现 RPC 模块中的代码需要进行优化,使得代码更加优雅,交互逻 ...

  10. Transformation-Based Error-Driven Learning and Natural Language Processing: A Case Study in Part-of-Speech Tagging

    http://delivery.acm.org/10.1145/220000/218367/p543-brill.pdf?ip=116.30.5.154&id=218367&acc=O ...