Dare To Think, Strive To Execute

Spark小白学习之路

背景

  最近好几个同学问我怎么开始学习spark, 虽说对spark的认识也十分有限,但是好歹接触spark也有一年半了,所以记录下自己的学习历程以便初入门者不至于最后“从入门到放弃”。想想当初自己抱着“hadoop权威指南”怀着一定要跟hadoop死磕到底的悲壮心情,不仅哑然失笑。刚开始的时候,都会觉得高大上的东西不大好懂,实则不然。刚开始大都是从看书开始的,理论知识又会让人觉得晦涩,顿时愈发觉得这些分布式框架高深莫测了。好在我学习spark没有那么痛苦,甚至可以说十分热爱这个分布式平台,遂分享下我的学习心得。

环境

spark

  下载spark稳定版的binary安装包,按照官方文档配置下spark_env.sh等配置文件。

  • tutorial: spark-tutorial
  • spark programming guide: programming-guide
    上面的教程直接在spark-shell中运行(不用 IDE)就好了。
    先看一遍试一遍有一个直观的感受。

    IDE

      intelliJ IDEA,该IDE功能十分强大,不仅集成了terminal, scala 编译器,sbt, 版本控制等, 还有代码美化,自动补全的功能。
    安装intelliJ IDEA, 然后安装sbt以及scala的编译器。
    error
      点击下面的plugin,搜索并安装set以及scala 插件。不过sbt的控件有时候会安装失败,如果多次失败,建议下载下来手动安装。至于sbt是做什么的,通俗点儿理解就是sbt是管理项目依赖的一个工具,spark程序依赖很多spark的jar包,如果我们配置了build.sbt那么sbt会自动去远程库中帮我们下载这些jar包,放在我们library中。sbt跟maven功能相同,但是前者服务于scala,后者主要用在java项目中。
      下载完了控件,接下来配置build.sbt文件。你在idea中新建一个项目的时候,首先项目下面就会出现一个build.sbt文件。如果只是简单的scala程序,不需要做额外配置,倘若是spark程序,可以按照如下格式添加依赖。
1
2
3
4
5
6
7
8
9
10
11
12
name := "example"
version := "0.1"
scalaVersion := "2.10.4"
libraryDependencies ++= Seq(
"org.apache.spark" %% "spark-core" % "1.5.2",
"org.apache.spark" %% "spark-graphx" % "1.5.2",
“org.apache.spark" %% "spark-streaming" % "1.5.2",
"org.apache.spark" %% "spark-mllib" % "1.5.2",
"org.apache.spark" %% "spark-sql" % "1.5.2" )

  上面我添加了5个依赖,这5个包都是spark生态的主要部门,分别是spark-core,这个只用用到RDD都需要使用,spark-graphx提供图数据的存储以及计算方面的API,spark-mllib中包含了一定数量的机器学习算法API,spark-streaming针对流式计算,spark-sql是spark生态不可低估的生力军,因为spark-sql支持多种数据源,可以处理hive,mysql中的数据,轻松实现ETL的操作。
上面的依赖包并不是越多越好,因为sbt每次创建新项目都会要下载这些依赖,一来比较慢,二来最后的jar包会很大。
语言, scala. 虽说spark支持python, R, Java, 但是一开始学习的时候用的就是scala就没觉得scala是学习障碍,况且scala的函数式编程功能在写map, reduce函数的时候真的十分便捷。另外scala跟java可以混合编程,scala文件中可以import java的包。
学习要点

driver跟executor的区别

  简单理解,driver运行主程序,如果有map等操作,都交给executor来执行。
分布式计算框架的核心思想就是要找可以并行的部分,然后将这些部分分给其他的计算机来执行,以此来提升运行的效率。想到这里看到其他的概念,就会清晰很多。当初碰到driver,executor, master, worker这些概念的时候也是一头雾水。
transformation 跟 action

  transformation跟action都是spark提供给RDD的API的分类。RDD提供给开发者的API有:

  • transformation: map, join, groupby, filter, flatmap, union, intersection, distinct……
  • action: reduce, sum, first, top, collect, take……
  • spark是内存计算,也是懒惰计算,就是所有的计算资源都尽可能load进内存中,transformation不会立即执行,但是出现action以后该action依赖的transformation才会一起执行。spark在运行期间会一直保存RDD的依赖信息,也叫血缘(lineage)。当然如果是迭代算法,需要好好考虑怎么优化,都则会出现OOM。

简单的spark程序 wordcount

1
2
3
4
5
6
7
8
9
10
11
12
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
object wordcount {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("wordcount").setMaster("local[2]")
val sc = new SparkContext(conf)
sc.textFile("text").flatMap{ s => s.split("\\s+")}.map{word => (word, 1)}.reduceByKey(_ + _).foreach(println)
sc.stop()
}
}

  处理小数据量的程序在idea就可以胜任(指定master为local), 数据量很大的情况可以打成jar包上传到spark集群中运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
output=hdfs://bda00:8020/user/xx/hive_table
table=test
function run {
hadoop fs -rm $output
spark-submit \
--executor-memory 5G \
--master "spark://hostname:7077" \
--class "sparksql.reader.HiveReader" \
--conf "spark.cores.max=10" \
jarName.jar \
--output_pt $output \
--table src
}
run

  这个jar包之前的是spark环境参数,例如executor的core数目,内存大小等,当然这个还可以传入依赖的jar包等。具体使用详见, spark_submit_application.–class指定spark程序主类。

  spark程序的参数是使用了scopt来实现读参数的,这个看起来比较美观,还可以进行参数检查,简单的参考java程序的传参方法(貌似就是jar包后面空格隔开)就好。

总结

  实践是了解spark最直接最有效的学习方式,如果想进一步了解spark,实践这些文档中的例子还远远不够,这个时候再看一些相关的技术书籍或者博客肯定是非常必要的。当我们对spark有了一定的认识,再了解它就不会那么恐惧了。

  另外补充一些非常不错的进阶资料,当然最好的还是官方文档: