spark 一、编程指南
总览
第一、每个spark 应用都有一个驱动程序去运行着主函数和再每个节点上的并行操作。
spark提供了一个RDD(弹性分布式数据集)的数据集合,可以通过不同的节点并行操作运算,可以通过hdfs文件构建。RDD可以在内存中进行缓存,当需要复用的时候会有更高的效率。
第二、提供了共享变量(shared varibales)在不同节点的并行操作中使用。一个是广播变量(broadcast variables)一个是累加器(accumulators)。当一个变量需要在不同节点任务重共享或者节点任务跟驱动程序见有变量共享需求时,可以用这俩工具。
连接spark
from pyspark import SparkContext, SparkConf
- SparkContext: 用来连接集群节点的一个接口
- SparkConf:
- spark应用的配置,将各种spark参数设置为key-value对。程序启动时会从系统中自动读取各种配置参数,若SparkConf有配置则以SparkConf优先。
- 设置
SparkConf(false),可以避免加在外部系统设置,完全以SparkConf为准。 - 所有的配置设置支持链式写法,比如:
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:
- HDFS
- Cassandra
- HBase
- Amazon S3
- local
spark支持直接读取 text files, SequenceFiles以及其他hadoop输入格式。
使用spark读取文件需要注意以下几点:
- 若读取路径为本地路径,需要保证节点上的相同路径必须是存在且可访问的。
- 所有spark读取文件操作比如sc.textFile 全都支持对文件夹、压缩文件及通配符文件名的支持。比如:
rdd = sc.textFile("/my/directory")
rdd = sc.textFile("/my/directory/*.txt")
rdd = sc.textFile("/my/directory/*.gz")
其他:
SparkContext.wholeTextFiles
支持从一个文件夹中读取很多小文件,按照(filename, content)的pairs返回RDD。相反,sc.textFile的方法读取文件夹会返回一个RDD, 每个文件的每一行都是一条 RDD record。。RDD.saveAsPickleFile和SparkContext.pickleFile
用于将RDD序列化为python对象的格式。- 保存和加载 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())
- 保存和加载其他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 支持异步操作,比如针对foreach的foreachAsync。
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效率间的取舍。建议按照以下几个原则进行选择:
- 默认的MEMORY_ONLY是CPU高效率的选择,如果程序运行良好,尽量不要改。
- 若程序运行性能不佳,尝试 MEMORY_ONLY_SER(仅适用于JAVA/SCALA)
- 如果要快速修复故障,使用基于复制的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)
两种方式
- 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。
- 继承 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 一、编程指南的更多相关文章
- Spark Graphx编程指南
问题导读1.GraphX提供了几种方式从RDD或者磁盘上的顶点和边集合构造图?2.PageRank算法在图中发挥什么作用?3.三角形计数算法的作用是什么?Spark中文手册-编程指南Spark之一个快 ...
- Apache Spark 2.2.0 中文文档 - Spark Streaming 编程指南 | ApacheCN
Spark Streaming 编程指南 概述 一个入门示例 基础概念 依赖 初始化 StreamingContext Discretized Streams (DStreams)(离散化流) Inp ...
- <译>Spark Sreaming 编程指南
Spark Streaming 编程指南 Overview A Quick Example Basic Concepts Linking Initializing StreamingContext D ...
- Apache Spark 2.2.0 中文文档 - Spark Streaming 编程指南
Spark Streaming 编程指南 概述 一个入门示例 基础概念 依赖 初始化 StreamingContext Discretized Streams (DStreams)(离散化流) Inp ...
- Spark—GraphX编程指南
Spark系列面试题 Spark面试题(一) Spark面试题(二) Spark面试题(三) Spark面试题(四) Spark面试题(五)--数据倾斜调优 Spark面试题(六)--Spark资源调 ...
- Spark Streaming编程指南
Overview A Quick Example Basic Concepts Linking Initializing StreamingContext Discretized Streams (D ...
- Spark SQL编程指南(Python)
前言 Spark SQL允许我们在Spark环境中使用SQL或者Hive SQL执行关系型查询.它的核心是一个特殊类型的Spark RDD:SchemaRDD. SchemaRDD类似于传统关 ...
- Spark SQL编程指南(Python)【转】
转自:http://www.cnblogs.com/yurunmiao/p/4685310.html 前言 Spark SQL允许我们在Spark环境中使用SQL或者Hive SQL执行关系型查询 ...
- Spark官方3 ---------Spark Streaming编程指南(1.5.0)
Design Patterns for using foreachRDD dstream.foreachRDD是一个强大的原语,允许将数据发送到外部系统.然而,了解如何正确有效地使用该原语很重要.避免 ...
- Spark(1.6.1) Sql 编程指南+实战案例分析
首先看看从官网学习后总结的一个思维导图 概述(Overview) Spark SQL是Spark的一个模块,用于结构化数据处理.它提供了一个编程的抽象被称为DataFrames,也可以作为分布式SQL ...
随机推荐
- 【EXPDP】Oracle expdp中并行问题
$ expdp hr/hr tables=test1 dumpfile=test2.dmp directory=pump parallel=4 Export: Release 11.2.0.4.0 - ...
- 类转json的基类实现
类转json的基类实现 项目地址 github地址 实现原理 使用反射获取类的属性名和属性内容.具体原理可以自己查一下资料 对一个类调用getClass().getDeclaredFields()可以 ...
- Netty学习:EventLoop事件机制
目录 EventLoop是什么 EventLoop适用的场景 Netty中的EventLoop Netty中的大量inEventLoop判断 Netty是如何建立连接并监听端口的-NIOSocketC ...
- [Usaco2005 Mar]Out of Hay 干草危机
题目描述 Bessie 计划调查N (2 <= N <= 2,000)个农场的干草情况,它从1号农场出发.农场之间总共有M (1 <= M <= 10,000)条双向道路,所有 ...
- 获取网页url中的参数
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 获取Java线程转储的常用方法
1. 线程转储简介 线程转储(Thread Dump)就是JVM中所有线程状态信息的一次快照. 线程转储一般使用文本格式, 可以将其保存到文本文件中, 然后人工查看和分析, 或者使用工具/API自动分 ...
- Scalable Go Scheduler Design Doc
https://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw/ Scalable Go Schedul ...
- 使用Linux服务器来通过网络安装和激活Windows 7 —— 一些基本原理
使用Linux服务器来通过网络安装和激活Windows 7 -- 一些基本原理 https://www.pufengdu.org/blog/?p=372
- JavaScript基础知识-基本概念
typeof操作符 typeof 操作符返回一个字符串,表示未经计算的操作数的类型. // 数值 typeof 37 === 'number'; typeof 3.14 === 'number'; t ...
- XV6学习(8)中断和设备驱动
驱动是操作系统中用于管理特定设备的代码:驱动控制设备硬件,通知硬件执行操作,处理中断,与等待该设备IO的进程进行交互. 当设备需要与操作系统进行交互时,就会产生中断(陷阱的一种),之后内核的陷阱处理代 ...