spark 学习笔记
- spark介绍
Spark是是一种快速通用的集群计算系统,它的主要特点是能够在内存中进行计算。它包含了 spark 核心组件 spark-core,用于 SQL 和结构化处理数据的 sparkSQL,流式计算 spark Stream,机器学习库 MLlib,和图形计算 GraphX。并且 spark 还提供了丰富的API,如 Java,Scala, Python, R 等提供接口,还有可以与其它的大数据工具进行密切的配合。 - spark 运行架构:
spark 的运行架构包含了每个应用的控制节点(driver),集群资源管理器(Cluster Manager)和多个个工作节点(Word Node)。每个 spark 应用都有一个驱动器程序(driver program)来发起集群上的各种并行操作,驱动器程序用一个sparkContext对象来访问spark。sparkContext 与 Cluster Manager 通信,以及 Excutor 的资源申请,任务的调度分配和监控。每个工作节点都有一个负责执行任务调度的 Excutor 进程(多线程的方式运行任务),缓存块用来缓存和各个任务。
每个 spark 应用程序执行时,先有 sparkContext 和 Cluster Manager 注册并申请资源,然后根据 RDD 创建 DAG(有向无环图),然后根据 RDD 间的依赖关系(宽依赖和窄依赖)划分为多个阶段(stage),再将各个 stage 提交给任务调度器(Task Scheduler),然后各个工作节点(word node)向 sparkContext 申请任务,任务调度器将各个 stage 分配给 Eecutor,并且 sparkContext 将应用程序代码发送过去。Excutor 将执行结果反馈给 DAG 调度器,运行结束后写入数据并释放资源。 - spark 的运行方式
这里只讲在 linux 系统上的部署。
- 本地模式:只需要在系统下载 spark 并且在系统上配上 JAVA_HOME,SPARK_HOME 的路径, 然后在 运行 spark-shell (在spark根目录下的bin中)看看能不能启动即可。
- 伪分布模式:不讨论,没配。
- Standalone模式:配置 spark 根目录下的 conf 目录中的slaves和spark-env.sh,然后启动start-all.sh 服务。
注:配置 spark-env.sh: SPARK_MASTER_HOST (将主服务器绑定到特定的主机名或IP地址,例如公共主机名或IP地址。如:localhost)
SPARK_MASTER_PORT (配置端口)
slaves:配置 worker 节点。
启动的时候可以用 sbin 目录下的 start-master.sh 和start-slave.sh <master-spark-URL> 分别启动 master 和 worker 。或者之间用 start-all.sh 启动。可以使用 netstat -tnlp | grep 7077(默认配置 master 的端口)查看端口占用。lsof -i:7077 -n 可以查看 master 和 slave 的交互情况。 - onYARN 模式:该模式是运行在 HDFS 上的完全分布式,由 YARN 来担任集群资源管理器。onYARN 有两种部署模式:①cluster:集群模式。该模式下,不管在哪台机器提交,都通过Yarn进行分配机器启动Driver。②client:客户端模式。该模式下,在哪台机器提交,就在哪台机器上启动Driver。(适合用于测试分布式情况)
注:配置 spark-env.sh 中的 HADOOP_CONF_DIR 用于指到 hadoop 集群配置文件的目录。(或YARN_CONF_DIR)。启动spark应用程序时,应指定 --master 的参数 为 yarn (因为主服务器为yarn),并且选择模式:–deploy-mode: cluster(client)。如:./sbin/spark-submit –master yarn --deploy-mode cluster --class org.apache.spark.examples.SparkPi examples/jars/spark-examples_2.11-2.4.0.jar 。
cluster模式下,结果果不在控制台上输出。查看运行结果,进入hadoop目录下logs/userlogs,然后进入最新文件目录。 - Mesos 模式:没配,不讨论。
- spark 共享变量
一般来说,当函数被传递到集群节点上运行的时候,函数中所有的变量都会被复制到每一台机器上,并且不会远程更新到 driver program。spark 为此提供了两种共享变量,一种是广播变量,一种是累加器。
广播变量:
val broadcast = sc.broadcast(Array(1, 2, 3))
broadcast.value // Array(1,2,3)
广播变量运行在每台机器上保留一个只读变量,而不是随函数一起发送它的副本。例如在每个节点都需要相同的大的输入集的时候,我们可以这么使用它。spark 自动广播每个阶段中任务所需的数据。以这种方式广播的数据以序列化形式缓存并在运行每个任务之前反序列化。这意味着显式创建广播变量仅在跨多个阶段的任务需要相同数据或以反序列化形式缓存数据很重要时才有用。
当广播变量创建后,在集群上运行任何函数我们都应该使用广播变量的值(broadcast.value),这样能确保其不会被多次传播,在创建广播变量后,我们不应该修改其值,以保证所有节点都使用相同的值。
累加器
累加器是仅通过关联和交换操作来添加的变量,可以使用它来实现计数器或者求和。可以通过 longbroadcast 和 Doublebroadcast 来创建数值类累加器。运行在集群上的任务可以通过 add 方法来添加它。但是无法读取它的value。只有 driver program 可以读取它的 value。
(可在 web UI 上跟踪该累加器)
val accum = sc.longbroadcast("My accumulator")
val array = sc.parallelize(Array(1, 2, 3, 4, 5))
array.foreach(x => accum.add(x))
println(accum.value)
用户也可以自己继承AccumulatorV2 抽象类来实现自己的累加器,但需要实现它的几个方法。
实现数学向量的累加。
class VectorAccumulatorV2 extends AccumulatorV2[MyVector, MyVector] {
private val myVector: MyVector = MyVector.createZeroVector
def reset(): Unit = {
myVector.reset()
}
def add(v: MyVector): Unit = {
myVector.add(v)
}
...
}
// Then, create an Accumulator of this type:
val myVectorAcc = new VectorAccumulatorV2
// Then, register it into spark context:
sc.register(myVectorAcc, "MyVectorAcc1")
- RDD 持久化
spark 重要的一个功能之一就是能够跨操作在内存中持久化(缓存)数据集。当持久化一个 RDD 时,每个节点的任何分区都会储存它计算的值,并在需要使用到它的时候直接读取,而不用重新计算。可以使用 persist() 和 cache() 来持久化 RDD。在第一次行动操作的时候持久化。持久化具有容错性,任何分区丢失了它,它将重新计算。可以通过传递 StorageLevel 对象设置持久化等级。
storageLevel | meaning |
---|---|
MEMORY_ONLY | 将RDD储存为JVM中的反序列java对象,如果它不适合被保存在内存中,则不会保存,并在需要的时候重新计算。 |
MEMORY_AND_DISK | 将RDD储存为JVM中的反序列java对象,如果它不适合被保存在内存中,就把不适合内存的那部分保存在磁盘中,并且在需要的时候读取他们。 |
MEMORY_ONLY_SER | |
(Java and Scala) | 将RDD存储为java序列对象,比反序列对象更节省空间,但读取是需要更多的CPU资源 |
MEMORY_AND_DISK_SER | |
(Java and Scala) | 与MEMORY_ONLY_SER类似,但将不适合存储在内存的分区溢出到磁盘,而不是每次需要时动态重新计算它们。 |
DISK_ONLY | 仅将RDD分区存储在磁盘上 |
MEMORY_ONLY_2, MEMORY_AND_DISK_2 | Same as the levels above, but replicate each partition on two cluster nodes.(不懂) |
OFF_HEAP (experimental) | 与MEMORY_ONLY_SER类似,但将数据存储在堆外内存中,这需要启用堆外内存 |
Spark会自动监视每个节点上的缓存使用情况,并以最近最少使用(LRU)的方式删除旧数据分区。如果想手动删除RDD而不是等待它退出缓存,可以使用 RDD.unpersist() 方法。