安装
Spark下载地址:http://spark.apache.org/downloads.html, 解压后即可使用。
创建测试文件并输出内容:
输入val lines=sc.textFile("/home/lucy/hellospark") 加载文件内容,输入lines.count()进行统计行数:
Scala之HelloWorld
环境:idea + maven + scala
创建一个空的maven工程(可参照参考资料1),在src/main目录下创建scala目录,并设置为Sources Root。然后添加Scala环境:
File->Project Structure->Libraries,新增Scala SDK库(如果是多模块的maven项目,则在需要的模块中添加)。
在scala目录下新建scala class文件,类型选择Object:
object HelloWorld {
def main(args: Array[String]): Unit = {
println("hello world!")
}
}
运行后即可输出hello world。
第一个Spark程序
对官方提供的例子WordCount进行稍许修改,如下:
import org.apache.spark.{SparkConf, SparkContext}
object WordCount {
def main(args: Array[String]): Unit = {
/**
* 创建Spark的配置对象SparkConf,设置Spark程序的运行时的配置信息
* 例如说通过setMaster来设置程序要连接的Spark集群的Master的URL
* 如果设置为local,则代表Spark程序本地运行
*/
val conf = new SparkConf()
conf.setAppName("MySimpleApp") // 设置应用名称,在程序运行的监控界面可以看到名称
conf.setMaster("local") // 此时程序在本地运行,无需安装集群
/**
* SparkContext是Spark程序所有功能的唯一入口
* 作用:初始化Spark应用程序运行所需要的核心组件,同时负责Spark程序往Master注册程序
* SparkContext是整个Spark应用程序中最为重要的一个对象
*/
val sc = new SparkContext(conf) // 创建SparkContext对象,通过传入SparkConf实例来定制Spark运行的具体参数
/**
* 根据具体的数据来源(HDFS,HBase,Local FS,DB等),通过SparkContext来创建RDD
* RDD的创建基本有三种方式,根据外部的数据来源,根据Scala集合,由其他的RDD操作产生
* 数据会被RDD划分为一系列的Partitions,分配到每个Partition的数据属于一个Task的处理范畴
*/
// 读取本地文件,并设置partition为1
var lines = sc.textFile("/home/文档/test_data", 1)
/**
* 对初始化的RDD进行Transformation级别的处理,例如map,filter等高阶函数等的编程。
* 1.将每一行的字符串拆分成单个单词
* 2.在单词拆分的基础上对每个单词的实例计数为1,也就是word=>(word,1)
* 3.在每个单词实例计数为1的基础之上统计每个单词在文件出现的次数
*/
// 对每一行的字符串进行单词的拆分并把所有行的拆分结果通过flat合并成一个大的单词集合
val words = lines.flatMap{
line => line.split(" ") //words同样是RDD类型
}
val pairs = words.map{
word => (word, 1)
}
val wordCounts = pairs.reduceByKey(_+_) // 对相同的key,进行value的累加
wordCounts.foreach(wordNumberPair => println(wordNumberPair._1 + " : " + wordNumberPair._2))
sc.stop() // 注意一定要将SparkContext的对象停止,因为SparkContext运行时会创建很多的对象
}
}
事实上在本地运行这个sample可能会报错。
第一个错误是: ClassNotFoundException: org.apache.spark.SparkConf
。
原因是依赖包org.apache.spark的作用域,去掉provided限制即可:
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
第二个错误是:
Exception in thread "main" java.lang.NoSuchMethodException: akka.remote.RemoteActorRefProvider
原因是:本地安装的scala中带的actor版本和spark中akka的actor版本有差异。解决办法是在File->Project Structure->modules中调整scala的sdk顺序,放到spark后即可,如下图所示:
其他可能遇到的错误:tried to access method com.google.common.base.Stopwatch.()V from class org.apache.hadoop.mapred.FileInputFormat
解决思路:hadoop包版本问题<dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>2.7.2</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>2.7.2</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-mapreduce-client-core</artifactId> <version>2.7.2</version> </dependency>
Spark基础函数用法
重要概念
RDD是Spark的计算模型,全称为弹性的分布式数据集合(Resilient Distributed DataSet)。RDD的创建基本有三种方式:从外部存储系统中引用一个数据集;并行化一个已存在的集合;由其他的RDD操作产生。
一个完整的RDD运行任务被分为两部分:Transformation(转换)和Action(执行)。
Transformation用于创建RDD,此部分提供了大量的操作方法,如map、filter、groupBy、join等,以便利用这些操作生成新的RDD。
Action是数据执行部分,通过count、reduce、collect等方法真正执行数据的计算部分。
RDD中所有的操作都是Lazy模式的,运行在编译中不会立即计算最终结果,而是记住所有的操作步骤和方法,只有显式地遇到启动命令时才执行计算。
Transformation操作
filter
filter操作对RDD进行过滤,生成一个新的RDD,原RDD不会改变。
Union或++
union操作(或++操作)用于合并两个RDD。
map
map操作将函数作用到RDD中的每个元素上,生成一个新的RDD返回。
下面有一个例子:
def main(args: Array[String]): Unit = {
val sc = new SparkContext("local", "My Opts")
// 根据集合产生RDD
val myfirstrdd = sc.parallelize(List("a", "b", "c"))
val ardd = myfirstrdd.filter(_ != "a").map(x => {(x, 1)})
// 设置持久化策略
ardd.persist(StorageLevel.DISK_ONLY)
// 保存
ardd.saveAsTextFile("output")
}
执行main函数,将在当前工作目录下生成output文件夹,含两个文件part-00000和_SUCCESS(执行成功标识,内容为空)。part-00000内容是:
(b,1)
(c,1)
flatMap
flatMap会先执行map操作,再将所有对象合并为一个对象,返回值是一个Sequence。flatMap将输入执行func操作时,对象必须是可迭代的。
下面看一个例子:
def main(args: Array[String]): Unit = {
val sc = new SparkContext("local", "My Opts")
val myfirstrdd = sc.parallelize(List("Hello world", "Hello master"))
val ardd = myfirstrdd.filter(_ != null).map(
line => {
val data = line.split(" ")
(data(0), data(1))
}
)
val result = ardd.collect()
println(result.toList)
ardd.persist(StorageLevel.DISK_ONLY)
// 保存
ardd.saveAsTextFile("output")
}
输出结果:List((Hello,world), (Hello,master)),生成的文件为:
(Hello,world)
(Hello,master)
如果使用flatMap,则如下所示:
def main(args: Array[String]): Unit = {
val sc = new SparkContext("local", "My Opts")
val myfirstrdd = sc.parallelize(List("Hello world", "Hello master"))
val ardd = myfirstrdd.filter(_ != null).flatMap(
line => {
line.split(" ")
}
)
val result = ardd.collect()
println(result.toList)
ardd.persist(StorageLevel.DISK_ONLY)
// 保存
ardd.saveAsTextFile("output")
}
输出结果:List(Hello, world, Hello, master),生成的文件为:
Hello
world
Hello
master
Action操作
count
count操作求RDD大小。如上文的例子中经过map和flatMap转换后的RDD,对其执行count(),结果分别是2和4。
countByValue
返回RDD每个元素的个数。对上文例子中经过flatMap转换后的RDD执行countByValue():
val result = ardd.countByValue()
println(result)
结果是:
Map(Hello -> 2, master -> 1, world -> 1)
reduce
reduce操作用于结果合并计算。
仍然对上文中经过flatMap转换后的RDD进行操作:
val result = ardd.reduce(
(x, y) => {x + "," + y}
)
println(result)
结果为:Hello,world,Hello,master
reduceByKey
reduceByKey根据key进行结果合并。
对于上文中经过flatMap转换后的RDD继续操作:
val secondRdd = ardd.map(line => {
(line, 1)
}).reduceByKey((x, y) => x + y)
println(secondRdd.collect().toList)
结果为:List((Hello,2), (master,1), (world,1))
程序打包与运行
开发完成之后,就可以将程序打成jar包并在spark中运行了。具体步骤是(IDE是idea):
(1) File->Project Structure->Artifacts,点击+按钮,添加JAR,如下图所示:
(2) 然后选择主类(点击文件夹图标自动提供选择项):
(3) 配置jar包生成路径Output directory,点击应用,OK。
(4) 选择菜单栏的Build选择,Build->Build Artifacts:
Build即可。最终生成的jar包位于第三步中配置的位置。
将jar包上传到spark服务器的某个目录,然后进入spark的bin目录,通过spark-submit运行jar包:
当然,对于maven工程,直接执行maven的package来生成jar包,更为方便。
参考资料
[1] https://blog.csdn.net/a18852867035/article/details/82744433
[2] https://blog.csdn.net/a1610770854/article/details/51810124
[3] https://www.jianshu.com/p/c6f8b56d8467