总览

第一、每个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. 【Linux】tcpdump

    tcpdump介绍 tcpdump 是一个运行在命令行下的抓包工具.它允许用户拦截和显示发送或收到过网络连接到该计算机的TCP/IP和其他数据包.tcpdump 适用于 大多数的类Unix系统操作系统 ...

  2. ABAP中SQL语句,指定索引(oracle)

    ①常用的两种方法: 1.指定使用全表扫描:%_HINTS ORACLE 'FULL(table_name)' 表示扫描整个表 2.指定索引:%_HINTS ORACLE 'INDEX("ta ...

  3. file转化为binary对象发送给后台

    具体代码如下: function filechange(e) { var file = $('#filed').get(0).files[0]; var fileSize = file.size, f ...

  4. DHCP中继配置

    (三台都需要关闭防火墙 前两台需要安装dhcp ) 第一台linux(vmnet2)(192.168.1.1) vim /etc/sysconfig/network-scripts/ifcfg-ens ...

  5. 将ffmpeg编译为wasm版本且在浏览器中运行

    2020年大前端技术趋势解读 原创 IMWeb团队 腾讯IMWeb前端团队 5天前

  6. High Performance Networking in Google Chrome 进程间通讯(IPC) 多进程资源加载

    小结: 1. 小文件存储于一个文件中: 在内部,磁盘缓存(disk cache)实现了它自己的一组数据结构, 它们被存储在一个单独的缓存目录里.其中有索引文件(在浏览器启动时加载到内存中),数据文件( ...

  7. DPDK CAS(compare and set)操作

    前言 rte_ring是一个无锁队列,无锁队列的出队入队操作是rte_ring实现的关键.因此,本文主要讲解dpdk是怎样使用无锁机制实现rte_ring的多生产者入队操作. rte_atomic32 ...

  8. Python学习【第2篇】:基本数据类型(详解)

    1.数字 2.字符串中的方法 str test = "xiaoxing"#首字母大写v = test.capitalize()print(v)运行后结果如下Xiaoxing tes ...

  9. UVA11694 Gokigen Naname题解

    目录 写在前面 Solution Code 写在前面 UVA的题需要自己读入一个 \(T\) 组数据,别被样例给迷惑了 Solution 每个格子只有两种填法且 \(n \le 7\),暴力搜索两种填 ...

  10. Kafka客户端Producer与Consumer

    Kafka客户端Producer与Consumer 一.pom.xml 二.相关配置文件 producer.properties log4j.properties base.properties 三. ...