1. Spark核心概念
1.1 Spark簡介
Apache Spark是新興的一種快速通用的大規模數據處理引擎。它的優勢有三個方面:
- 通用計算引擎 能夠運行MapReduce、數據挖掘、圖運算、流式計算、SQL等多種框架
- 基于內存 數據可緩存在內存中,特別適用于需要迭代多次運算的場景
- 與Hadoop集成 能夠直接讀寫HDFS中的數據,并能運行在YARN之上
Spark是用Scala語言編寫的,所提供的API也很好地利用了這門語言的特性,當然作為數據科學的一環,它也可以使用Java和Python編寫應用。這里我們將用Python給大家做講解。
1.2 Spark核心
Spark支持多種運行模式。單機部署下,既可以用本地(Local)模式運行,也可以使用偽分布式模式來運行;當以分布式集群部署的時候,可以根據實際情況選擇Spark自帶的獨立(Standalone)運行模式、YARN運行模式或者Mesos模式。雖然模式多,但是Spark的運行架構基本由三部分組成,包括SparkContext(驅動程序)、ClusterManager(集群資源管理器)和Executor(任務執行進程)。
- SparkContext提交作業,向ClusterManager申請資源;
- ClusterManager會根據當前集群的資源使用情況,進行有條件的FIFO策略:先分配的應用程序盡可能多地獲取資源,后分配的應用程序則在剩余資源中篩選,沒有合適資源的應用程序只能等待其他應用程序釋放資源;
- ClusterManager默認情況下會將應用程序分布在盡可能多的Worker上,這種分配算法有利于充分利用集群資源,適合內存使用多的場景,以便更好地做到數據處理的本地性;另一種則是分布在盡可能少的Worker上,這種適合CPU密集型且內存使用較少的場景;
- Excutor創建后與SparkContext保持通訊,SparkContext分配任務集給Excutor,Excutor按照一定的調度策略執行任務集。
Spark包含1個driver(筆記本電腦或者集群網關機器上)和若干個executor(在各個節點上),通過SparkContext
(簡稱sc
)連接Spark集群
、創建RDD
、累加器(accumlator)
、廣播變量(broadcast variables)
,簡單可以認為SparkContext(驅動程序)是Spark程序的根本。
Driver會把計算任務分成一系列小的task,然后送到executor執行。executor之間可以通信,在每個executor完成自己的task以后,所有的信息會被傳回。
1.3 RDD(彈性分布式數據集)介紹
在Spark里,所有的處理和計算任務都會被組織成一系列Resilient Distributed Dataset(彈性分布式數據集,簡稱RDD)上的transformations(轉換) 和 actions(動作)。
RDD是一個包含諸多元素、被劃分到不同節點上進行并行處理的數據集合,可以將RDD持久化到內存中,這樣就可以有效地在并行操作中復用(在機器學習這種需要反復迭代的任務中非常有效)。在節點發生錯誤時RDD也可以自動恢復。
說起來,RDD就像一個NumPy array
或者一個Pandas Series
,可以視作一個有序的item集合。
只不過這些item并不存在driver端的內存里,而是被分割成很多個partitions,每個partition的數據存在集群的executor的內存中。
1.4 RDD transformations和actions
大家還對python的list comprehension有印象嗎,RDDs可以進行一系列的變換得到新的RDD,有點類似那個過程,我們先給大家提一下RDD上最最常用到的transformation:
map()
對RDD的每一個item都執行同一個操作flatMap()
對RDD中的item執行同一個操作以后得到一個list,然后以平鋪的方式把這些list里所有的結果組成新的listfilter()
篩選出來滿足條件的itemdistinct()
對RDD中的item去重sample()
從RDD中的item中采樣一部分出來,有放回或者無放回sortBy()
對RDD中的item進行排序
特別注意:Spark的一個核心概念是惰性計算。當你把一個RDD轉換成另一個的時候,這個轉換不會立即生效執行?。。park會把它先記在心里,等到真的需要拿到轉換結果的時候,才會重新組織你的transformations(因為可能有一連串的變換)
這樣可以避免不必要的中間結果存儲和通信。記住哦,transformation屬于多行計算
剛才提到了惰性計算,那么什么東西能讓它真的執行轉換與運算呢?
是的,就是我們馬上提到的Actions,下面是常見的action,當他們出現的時候,表明我們需要執行剛才定義的transform了:
-
collect()
: 計算所有的items并返回所有的結果到driver端,接著collect()
會以Python list的形式返回結果 -
first()
: 和上面是類似的,不過只返回第1個item -
take(n)
: 類似,但是返回n個item -
count()
: 計算RDD中item的個數 -
top(n)
: 返回頭n個items,按照自然結果排序 -
reduce()
: 對RDD中的items做聚合
1.5 針對更復雜的transformations和actions
咱們剛才已經見識到了Spark
中最常見的transform和action,但是有時候我們會遇到更復雜的結構,比如非常非常經典的是以元組形式組織的k-v對(key, value)
我們把它叫做pair RDDs,而Sark中針對這種item結構的數據,定義了一些transformation和action:
-
reduceByKey()
: 對所有有著相同key的items執行reduce操作 -
groupByKey()
: 返回類似(key, listOfValues)元組的RDD,后面的value List 是同一個key下面的 -
sortByKey()
: 按照key排序 -
countByKey()
: 按照key去對item個數進行統計 -
collectAsMap()
: 和collect有些類似,但是返回的是k-v的字典
2. PySpark之詞頻統計
首先,我們導入pyspark的包,創建SparkContext,建立RDD
import pyspark
from pyspark import SparkContext
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
然后讀取文本文件
textFile = sc.textFile("file:///usr/local/spark/mycode/wordcount/word.txt")
textFile是一個方法,可以用來加載文本數據,默認是從HDFS上加載,如果要加載本地文件,就必須使用file:///加路徑的形式
從文本中讀取數據后就要開始進行詞頻統計了
wordCount = textFile.flatMap(lambda line:line.split(" ")).\
map(lambda word:(word, 1)).reduceByKey(lambda x,y:x+y)
flatMap會逐行遍歷文本內容,然后對每行內容進行lambda函數的操作,即line:line.split(" "),該操作會把每一行內容賦值給line,然后對每一個line進行split(" ")操作,即對每一行用空格分隔成單獨的單詞,這樣每一行都是一個由單詞組成的集合,因為有很多行,所以就有很多歌這樣的單詞集合,執行完 textFile.flatMap(lambda line:line.split(" "))后會把這些單詞集合組成一個大的單詞集合
map(lambda word:(word, 1))中的map對上述產生的單詞集合進行遍歷,對于每一個單詞進行map函數內的操作,即lambda word:(word,1),該操作會把每個單詞賦值給word,然后組成一個鍵值對,這個鍵值對的key是這個單詞,而value是1,這樣就把每個單詞變成了這個單詞的鍵值對形式。執行完這個map后就會獲得一個RDD,這個RDD的每一個元素是很多個鍵值對
reduceByKey(lambda x,y:x+y)會對RDD中的每個元素根據key進行分組,然后對該分組進行括號內的操作,即lambda x,y:x+y,通過對具有相同key的元素進行該操作,reduce操作會把具有相同key的元素的value進行相加,這樣最后變成一個大的鍵值對,key是相同的,value是具有相同key的鍵值對的個數,這樣,詞頻統計就完成了。