1. 什麼是RDD ?
彈性分佈式數據集(Resilient Distributed Dataset,RDD),就是分佈式的元素集合.
在 Spark 中,對數據的所有操作不外乎創建 RDD、轉化已有 RDD 以及調用 RDD 操作進行求值
1.1 RDD基礎
兩種方法創建 RDD:
1. 讀取一個外部數據集;
2. 在驅動器程序裏分發驅動器程序中的對象集合(比如 list 和 set)。
示例:
// 在 Python 中使用 textFile() 創建一個字符串的 RDD
>>> lines = sc.textFile("README.md")
RDD 支 持 兩 種 類 型 的 操 作:
1 . 轉 化 操 作(transformation).
轉化操作會由一個 RDD 生成一個新的 RDD.
// 調用轉化操作 filter()
>>> pythonLines = lines.filter(lambda line: "Python" in line)
2 . 行 動 操 作(action)
行動操作會對 RDD 計算出一個結果,並把結果返回到驅動器程序中,或把結果存儲到外部存儲系統(如 HDFS)中.
// 調用 first() 行動操作
>>> pythonLines.first()
u'## Interactive Python Shell'
在實際操作中,你會經常用 persist() 來把數據的一部分讀取到內存中,並反覆查詢這部分數據.
// 把 RDD 持久化到內存中
>>> pythonLines.persist
>>> pythonLines.count()
2
>>> pythonLines.first()
u'## Interactive Python Shell'
總的來說,每個 Spark 程序或 shell 會話都按如下方式工作。
(1) 從外部數據創建出輸入 RDD。
(2) 使用諸如 filter() 這樣的轉化操作對 RDD 進行轉化,以定義新的 RDD。
(3) 告訴 Spark 對需要被重用的中間結果 RDD 執行 persist() 操作。
(4) 使用行動操作(例如 count() 和 first() 等)來觸發一次並行計算, Spark 會對計算進行優化後再執行。
2. 創建RDD
Spark 提供了兩種創建 RDD 的方式:讀取外部數據集,以及在驅動器程序中對一個集合進行並行化.
一種用得少的方法:
在學習Spark時候,在 shell 中快速創建出自己的 RDD, 然後對這些 RDD 進行操作
// Python 中的 parallelize() 方法
lines = sc.parallelize(["pandas", "i like pandas"])
另一種常見方法:
外部存儲中讀取數據來創建 RDD。
// Python 中的 textFile() 方法
lines = sc.textFile("/path/to/README.md")
3. RDD操作
RDD 支持兩種操作: 轉化操作和行動操作。
RDD 的轉化操作是返回一個新的 RDD 的操作,比如 map() 和 filter(),而行動操作則是向驅動器程序返回結果或把結果寫入外部系統的操作, 會觸發實際的計算,比如 count() 和 first()。
舉個例子,假定我們有一個日誌文件 log.txt, 內含有若干消息,希望選出其中的錯誤消息。我們可以使用前面說過的轉化操作 filter()。
// 用 Python 實現 filter() 轉化操作
inputRDD = sc.textFile("log.txt")
errorsRDD = inputRDD.filter(lambda x: "error" in x)
再從 inputRDD 中找出所有包含單詞 warning 的行。使用另一個轉化操作 union() 來打印出包含 error 或 warning 的行數。
// Python 進行 union() 轉化操作
errorsRDD = inputRDD.filter(lambda x: "error" in x)
warningsRDD = inputRDD.filter(lambda x: "warning" in x)
badLinesRDD = errorsRDD.union(warningsRDD)
3.1 轉化操作
RDD 的轉化操作是返回新 RDD 的操作.
此類操作有:
union()
// 用 Python 實現 filter() 轉化操作
inputRDD = sc.textFile("log.txt")
errorsRDD = inputRDD.filter(lambda x: "error" in x)
// 用 Python 進行 union() 轉化操作, 繼續使用inputRDD
errorsRDD = inputRDD.filter(lambda x: "error" in x)
warningsRDD = inputRDD.filter(lambda x: "warning" in x)
badLinesRDD = errorsRDD.union(warningsRDD) // 合併
3.2 行動操作
行動操作是第二種類型的 RDD 操作,它們會把最終求得的結果返回到驅動器程序, 或者寫入外部存儲系統中。
由於行動操作需要生成實際的輸出,它們會強制執行那些求值必須用到的 RDD 的轉化操作。
繼續日誌的例子,我們可能想輸出關於 badLinesRDD 的一些信息。
爲此,需要使用兩個行動操作來實現:
用 count() 來返回計數結果;
用 take() 來收集RDD 中的一些元素.
// 在 Python 中使用行動操作對錯誤進行計數
print "Input had " + badLinesRDD.count() + " concerning lines"
print "Here are 10 examples:"
for line in badLinesRDD.take(10):
print line
注意的是, 每當我們調用一個新的行動操作時,整個 RDD 都會從頭開始計算。要避免這種低效的行爲,用戶可以將中間結果持久化.
3.3 惰性求值
(概念性理解即可)
RDD 的轉化操作都是惰性求值的。這意味着在被調用行動操作之前 Spark 不會開始計算。Spark 使用惰性求值,這樣就可以把一些操作合併到一起來減少計算數據的步驟。
Spark 使用惰性求值,這樣就可以把一些操作合併到一起來減少計算數據的步驟。
4. 向Spark傳遞函數
這裏依舊以Python代碼示範.
word = rdd.filter(lambda s: "error" in s)
def containsError(s):
return "error" in s
word = rdd.filter(containsError)
5. 常見的轉化操作和行動操作
5.1 基本RDD
首先來講講哪些轉化操作和行動操作受任意數據類型的 RDD 支持。
針對各個元素的轉化操作
map() 的返回值類型不需要和輸入類型一樣。
flatMap() 對每個輸入元素生成多個輸出元素.一個簡單用途是把輸入的字符串切分爲單詞.
distinct() 去重
sample(withReplacement, fraction, [seed]) 對 RDD 採樣,以及是否替換僞集合操作
最簡單的集合操作是 union(other),它會返回一個包含兩個 RDD 中所有元素的 RDD。intersection(other) 方法,只返回兩個 RDD 中都有的元素。
subtract(other) 函數接收另一個 RDD 作爲參數,返回
一個由只存在於第一個 RDD 中而不存在於第二個 RDD 中的所有元素組成的 RDD。
cartesian() 與另一個 RDD 的笛卡兒積行動操作
最常見的行動操作 reduce(),接收一個函數作爲參數,這個函數要操作兩個 RDD 的元素類型的數據並返回一個同樣類型的新元素。
// Python 中的 reduce()
sum = rdd.reduce(lambda x, y: x + y)
fold() 和 reduce() 類似,接收一個與 reduce() 接收的函數簽名相同的函數,再加上一個”初始值”來作爲每個分區第一次調用時的結果。
5.2 在不同RDD類型間轉換
有些函數只能用於特定類型的 RDD.
Python 的 API 結構與 Java 和 Scala 有所不同。在 Python 中,所有的函數都實現在基本的RDD 類中,但如果操作對應的 RDD 數據類型不正確,就會導致運行時錯誤。