安裝
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