本文介紹兩點:
1. 如何用Spark讀取本文文件內容。
2. 如何用Spark將數據寫到本地,特別是將大文件寫到本地。
Spark讀取本地文件內容
通常情況下,如果用下麵的代碼讀取本地文件:
val data = sc.textFile("somefile.txt")
直接這樣寫,係統有可能會報錯,正確的讀取方式:
var data = sc.textFile("file:///path to the file/")
原因:SparkContext.textFile內部調用了org.apache.hadoop.mapred.FileInputFormat.getSplits
函數;當我們不設置textFile的文件模式的時候,getSplits函數又會調用org.apache.hadoop.fs.getDefaultUri
;getDefaultUri這個函數從Hadoop配置中讀取”fs.defaultFS”參數,這個參數通常會在HADOOP_CONF_DIR環境變量中設置為”hdfs://”, 而不是”file://”。”hdfs://”或者”file://”就是前麵說的文件模式,如果textFile中不指定,則默認為”hdfs://”。
當然,將文件先拷貝到hdfs,再textFile也是個不錯的選擇,使用hdfs fs -put命令:
${HADOOP_COMMON_HOME}/bin/hadoop fs -put /localpath/to/data.txt /hdfspath/to/data.txt
或者,使用hdfs fs -copyFromLocal命令:
${HADOOP_COMMON_HOME}/bin/hadoop fs -copyFromLocal /localpath/to/data.txt /hdfspath/to/data.txt
Spark寫/保存文件到本地
使用spark將hdfs上的文件保存到本地,如果文件較小,直接collect
或者println
都可以。如果文件較大,比如有好幾個GB,就需要細致處理了,因為直接collect很可能會打爆spark driver內存。
spark寫大文件到本地的正確方式應該是
1. 直接使用SparkAPI toLocalIterator(需要的本地最大內存等於最大分片占用內存):
val it = rdd.toLocalIterator
while(it.hasNext){
println(it.next)
}
2.或者遍曆每個partitions,分別collect寫到本地,自己寫邏輯(原理同上,可以自己控製做過濾之類的操作)
val parts = rdd.partitions
for (p <- parts) {
val idx = p.index
val partRdd = rdd.mapPartitionsWithIndex(a => if (a._1 == idx) a._2 else Iterator(), true)
//The second argument is true to avoid rdd reshuffling
val data = partRdd.collect //data contains all values from a single partition
//in the form of array
//Now you can do with the data whatever you want: iterate, save to a file, etc.
}
跟讀一樣,寫也可以先saveAsTextFile,然後使用hdfs命令存到本地, 使用hdfs fs -get命令:
${HADOOP_COMMON_HOME}/bin/hadoop fs -get /hdfspath/to/data.txt /localpath/to/data.txt
或者,使用hdfs fs -copyToLocal命令:
${HADOOP_COMMON_HOME}/bin/hadoop fs -copyToLocal /hdfspath/to/data.txt /localpath/to/data.txt
————————————————————————————————————————————————————————————————————————
延伸閱讀,附圖:spark client模式,讀寫本地文件一般需要client模式。