spark學習筆記--資料讀取與儲存
資料讀取與儲存
- 檔案格式與檔案系統
對於儲存在本地檔案系統或分散式檔案系統(比如 NFS、HDFS、Amazon S3 等)中的資料,Spark 可以訪問很多種不同的檔案格式,包括文字檔案、JSON、SequenceFile,以及 protocol buffer。我們會展示幾種常見格式的用法,以及 Spark 針對不同檔案系統的配置和壓縮選項。
- Spark SQL中的結構化資料來源
通過Spark SQL 模組,針對包括 JSON 和 Apache Hive 在內的結構化資料來源,提供了一套更加簡潔高效的 API。
資料庫與鍵值儲存
Spark 自帶的庫和一些第三方庫,它們可以用來連線 Cassandra、HBase、Elasticsearch 以及 JDBC 源。
支援的文字格式
文字檔案
- 讀取檔案
只需要使用檔案路徑作為引數呼叫 SparkContext 中的 textFile() 函式,就可以讀取一個文字檔案
如果要控制分割槽數的話,可以指定 minPartitions
scala:
val input = sc.textFile("file:///home/holden/repos/spark/README.md")
有時候我們希望同時處理多個檔案,除上述方法外,也可以用SparkContext.wholeTextFiles()
方法,該方法會返回一個 pair RDD,其中鍵是輸入檔案的檔名。
scala:
val input = sc.wholeTextFiles("file://home/holden/salesFiles")
val result = input.mapValues{y =>
val nums = y.split(" ").map(x => x.toDouble)
nums.sum / nums.size.toDouble}
- 儲存檔案
scala:
result.saveAsTextFile(outputFile)
JSON
- 讀取檔案
將資料作為文字檔案讀取,然後對 JSON 資料進行解析,這樣的方法可以在所有支援的程式語言中使用。這種方法假設檔案中的每一行都是一條 JSON 記錄。如果你有跨行的 JSON 資料,你就只能讀入整個檔案,然後對每個檔案進行解析。如果在你使用的語言中構建一個 JSON 解析器的開銷較大,你可以使用 mapPartitions() 來重用解析器。
scala:
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.DeserializationFeature
case class Person(name: String, lovesPandas: Boolean) // 必須是頂級類
// 將其解析為特定的case class。使用flatMap,通過在遇到問題時返回空列表(None)
// 來處理錯誤,而在沒有問題時返回包含一個元素的列表(Some(_))
val result = input.flatMap(record => {
try {
Some(mapper.readValue(record, classOf[Person]))
} catch {
case e: Exception => None
}})
- 儲存檔案
scala:
//篩選後寫入檔案
result.filter(p => P.lovesPandas).map(mapper.writeValueAsString(_)).saveAsTextFile(outputFile)
CSV(逗號分隔)和 TSV(製表符分隔)
- 讀取檔案
scala:
import Java.io.StringReader
import au.com.bytecode.opencsv.CSVReader
val input = sc.textFile(inputFile)
val result = input.map{ line =>
val reader = new CSVReader(new StringReader(line));
reader.readNext();
}
如果在欄位中嵌有換行符,就需要完整讀入每個檔案,然後解析各段
scala:
case class Person(name: String, favoriteAnimal: String)
val input = sc.wholeTextFiles(inputFile)
val result = input.flatMap{ case (_, txt) =>
val reader = new CSVReader(new StringReader(txt));
reader.readAll().map(x => Person(x(0), x(1)))
}
- 儲存檔案
scala:
pandaLovers.map(person => List(person.name, person.favoriteAnimal).toArray)
.mapPartitions{people =>
val stringWriter = new StringWriter();
val csvWriter = new CSVWriter(stringWriter);
csvWriter.writeAll(people.toList)
Iterator(stringWriter.toString)
}.saveAsTextFile(outFile)
SequenceFile
SequenceFile 是由沒有相對關係結構的鍵值對檔案組成的常用 Hadoop 格式。SequenceFile 檔案有同步標記,Spark可以用它來定位到檔案中的某個點,然後再與記錄的邊界對齊。這可以讓 Spark 使用多個節點高效地並行讀取 SequenceFile 檔案。
SequenceFile 也是 Hadoop MapReduce 作業中常用的輸入輸出格式,所以如果你在使用一個已有的 Hadoop 系統,資料很有可能是以 SequenceFile 的格式供你使用的。
由於 Hadoop 使用了一套自定義的序列化框架,因此 SequenceFile 是由實現 Hadoop 的 Writable 介面的元素組成。
- 讀取SequenceFile
可以用sequenceFile(path, keyClass, valueClass, minPartitions),其中keyClass 和 valueClass 引數都必須使用正確的 Writable 類
scala:
val data = sc.sequenceFile(inFile, classOf[Text], classOf[IntWritable]).
map{case (x, y) => (x.toString, y.get())}
- 儲存SequenceFile
因為 SequenceFile 儲存的是鍵值對,所以需要建立一個由可以寫出到 SequenceFile 的型別構成的 PairRDD。
我們已經進行了將許多 Scala 的原生型別轉為 Hadoop Writable 的隱式轉換,所以如果你要寫出的是 Scala 的原生型別,可以直接呼叫 saveSequenceFile(path) 儲存你的 PairRDD,它會幫你寫出資料。
如果鍵和值不能自動轉為 Writable 型別,或者想使用變長型別(比如 VIntWritable),就可以對資料進行對映操作,在儲存之前進行型別轉換
scala:
val data = sc.parallelize(List(("Panda", 3), ("Kay", 6), ("Snail", 2)))
data.saveAsSequenceFile(outputFile)
物件檔案
物件檔案看起來就像是對 SequenceFile 的簡單封裝,它允許儲存只包含值的 RDD。和 SequenceFile 不一樣的是,物件檔案是使用 Java 序列化寫出的。
讀取
用 SparkContext 中的 objectFile() 函式接收一個路徑,返回對應的 RDD。儲存
要儲存物件檔案,只需在 RDD 上呼叫 saveAsObjectFile 就行了
Hadoop輸入輸出格式
- 讀取其他Hadoop輸入格式
hadoopFile()
用於舊的API實現Hadoop輸入格式
每一行都會被獨立處理,鍵和值之間用製表符隔開。這個格式存在於 Hadoop 中,所以無需向工程中新增額外的依賴就能使用它。
scala:
val input = sc.hadoopFile[Text, Text, KeyValueTextInputFormat](inputFile)
.map{
case (x, y) => (x.toString, y.toString)
}
newAPIHadoopFile()
接收一個路徑以及三個類。第一個類是“格式”類,代表輸入格式。
scala:
//在 Scala 中使用 Elephant Bird 讀取 LZO 演算法壓縮的 JSON 檔案
val input = sc.newAPIHadoopFile(inputFile, classOf[LzoJsonInputFormat],
classOf[LongWritable], classOf[MapWritable], conf)
// "輸入"中的每個MapWritable代表一個JSON物件
- 儲存Hadoop輸出格式
舊介面用
saveAsHadoopFile()
,新介面用saveAsNewAPIHadoopFile()
非檔案系統資料來源可以使用hadoopDataset
/saveAsHadoopDataSet
和newAPIHadoopDataset
/saveAsNewAPIHadoopDataset
來訪問 Hadoop 所支援的非檔案系統的儲存格式。
scala:
val job = new Job()
val conf = job.getConfiguration
LzoProtobufBlockOutputFormat.setClassConf(classOf[Places.Venue], conf);
val dnaLounge = Places.Venue.newBuilder()
dnaLounge.setId(1);
dnaLounge.setName("DNA Lounge")
dnaLounge.setType(Places.Venue.VenueType.CLUB)
val data = sc.parallelize(List(dnaLounge.build()))
val outputData = data.map{ pb =>
val protoWritable = ProtobufWritable.newInstance(classOf[Places.Venue]);
protoWritable.set(pb)
(null, protoWritable)
}
outputData.saveAsNewAPIHadoopFile(outputFile, classOf[Text],
classOf[ProtobufWritable[Places.Venue]],
classOf[LzoProtobufBlockOutputFormat[ProtobufWritable[Places.Venue]]], conf)
檔案壓縮
在大資料工作中,我們經常需要對資料進行壓縮以節省儲存空間和網路傳輸開銷。對於大多數 Hadoop 輸出格式來說,我們可以指定一種壓縮編解碼器來壓縮資料
用
textFile()
和sequenceFile()
讀取檔案,最好不要考慮使用spark的封裝,使用newAPIHadoopFile()
或者hadoopFile()
,並指定正確的壓縮編解碼器。
檔案系統
本地/“常規”檔案系統
本地檔案要求在叢集中所有節點的相同路徑下都可以找到,路徑形式:file:// 路徑
Amazon S3
路徑形式:s3n:// 開頭的路徑以 s3n://bucket/path-within-bucket。s3支援萬用字元:s3n://bucket/my-Files/*.txt
HDFS
Hadoop 分散式檔案系統(HDFS)是一種廣泛使用的檔案系統,HDFS被設計為可以在硬體上工作,有彈性地應對節點失敗且提供高吞吐量。路徑形式:hdfs://master:port/path
Spark SQL中的結構化資料
在各種情況下,我們把一條 SQL 查詢給 Spark SQL,讓它對一個資料來源執行查詢(選出一些欄位或者對欄位使用一些函式),然後得到由 Row 物件組成的 RDD,每個 Row 物件表示一條記錄。在 Java 和 Scala 中,Row 物件的訪問是基於下標的。每個 Row 都有一個 get() 方法,會返回一個一般型別讓我們可以進行型別轉換。另外還有針對常見基本型別的專用 get() 方法(例如 getFloat()、getInt()、getLong()、getString()、getShort()、getBoolean() 等)
Apache Hive
Apache Hive 是 Hadoop 上的一種常見的結構化資料來源。Hive 可以在 HDFS 內或者在其他儲存系統上儲存多種格式的表。這些格式從普通文字到列式儲存格式,應有盡有。Spark SQL 可以讀取 Hive 支援的任何表。
scala:
import org.apache.spark.sql.hive.HiveContext
val hiveCtx = new org.apache.spark.sql.hive.HiveContext(sc)
val rows = hiveCtx.sql("SELECT name, age FROM users")
val firstRow = rows.first()
println(firstRow.getString(0)) // 欄位0是name欄位
JSON
如果你有記錄間結構一致的 JSON 資料,Spark SQL 也可以自動推斷出它們的結構資訊,並將這些資料讀取為記錄,這樣就可以使得提取欄位的操作變得很簡單。
scala:
val tweets = hiveCtx.jsonFile("tweets.json")
tweets.registerTempTable("tweets")
val results = hiveCtx.sql("SELECT user.name, text FROM tweets")
Spark連線資料庫
Java資料庫連線
Spark 可以從任何支援 Java 資料庫連線(JDBC)的關係型資料庫中讀取資料,包括 MySQL、Postgre 等系統。要訪問這些資料,需要構建一個 org.apache.spark.rdd.JdbcRDD,將 SparkContext 和其他引數一起傳給它。
scala:
//用於對資料庫建立連線的函式
def createConnection() = {
Class.forName("com.mysql.jdbc.Driver").newInstance();
DriverManager.getConnection("jdbc:mysql://localhost/test?user=holden");
}
//將輸出結果從 java.sql.ResultSet轉為對運算元據有用的格式的函式
def extractValues(r: ResultSet) = {
(r.getInt(1), r.getString(2))
}
//讀取一定範圍內資料的查詢,以及查詢引數中 lowerBound 和 upperBound 的值
val data = new JdbcRDD(sc,
createConnection, "SELECT * FROM panda WHERE ? <= id AND id <= ?",
lowerBound = 1, upperBound = 3, numPartitions = 2, mapRow = extractValues)
println(data.collect().toList)
Cassandra
隨著 DataStax 開源其用於 Spark 的 Cassandra 聯結器
Maven 依賴:
<dependency> <!-- Cassandra -->
<groupId>com.datastax.spark</groupId>
<artifactId>spark-cassandra-connector</artifactId>
<version>1.0.0-rc5</version>
</dependency>
<dependency> <!-- Cassandra -->
<groupId>com.datastax.spark</groupId>
<artifactId>spark-cassandra-connector-java</artifactId>
<version>1.0.0-rc5</version>
</dependency>
scala:
//配置Cassandra屬性
val conf = new SparkConf(true)
.set("spark.cassandra.connection.host", "hostname")
val sc = new SparkContext(conf)
//對整張鍵值對錶讀取為RDD
// 為SparkContext和RDD提供附加函式的隱式轉換
import com.datastax.spark.connector._
// 將整張表讀為一個RDD。假設你的表test的建立語句為
// CREATE TABLE test.kv(key text PRIMARY KEY, value int);
val data = sc.cassandraTable("test" , "kv")
// 列印出value欄位的一些基本統計。
data.map(row => row.getInt("value")).stats()
在scala中儲存資料到Cassandra
val rdd = sc.parallelize(List(Seq("moremagic", 1)))
rdd.saveToCassandra("test" , "kv", SomeColumns("key", "value"))
HBase
scala:
import org.apache.hadoop.hbase.HBaseConfiguration
import org.apache.hadoop.hbase.client.Result
import org.apache.hadoop.hbase.io.ImmutableBytesWritable
import org.apache.hadoop.hbase.mapreduce.TableInputFormat
val conf = HBaseConfiguration.create()
conf.set(TableInputFormat.INPUT_TABLE, "tablename") // 掃描哪張表
val rdd = sc.newAPIHadoopRDD(
conf, classOf[TableInputFormat], classOf[ImmutableBytesWritable],classOf[Result])
Elasticsearch
Spark 可以使用 Elasticsearch-Hadoop,從 Elasticsearch 中讀寫資料。Elasticsearch 是一個開源的、基於 Lucene 的搜尋系統。
在 Scala 中使用 Elasticsearch 輸出
val jobConf = new JobConf(sc.hadoopConfiguration)
jobConf.set("mapred.output.format.class", "org.elasticsearch.hadoop.
mr.EsOutputFormat")
jobConf.setOutputCommitter(classOf[FileOutputCommitter])
jobConf.set(ConfigurationOptions.ES_RESOURCE_WRITE, "twitter/tweets")
jobConf.set(ConfigurationOptions.ES_NODES, "localhost")
FileOutputFormat.setOutputPath(jobConf, new Path("-"))
output.saveAsHadoopDataset(jobConf)
在 Scala 中使用 Elasticsearch 輸入
def mapWritableToInput(in: MapWritable): Map[String, String] = {
in.map{case (k, v) => (k.toString, v.toString)}.toMap
}
val jobConf = new JobConf(sc.hadoopConfiguration)
jobConf.set(ConfigurationOptions.ES_RESOURCE_READ, args(1))
jobConf.set(ConfigurationOptions.ES_NODES, args(2))
val currentTweets = sc.hadoopRDD(jobConf,
classOf[EsInputFormat[Object, MapWritable]], classOf[Object],
classOf[MapWritable])
// 僅提取map
// 將MapWritable[Text, Text]轉為Map[String, String]
val tweets = currentTweets.map{ case (key, value) => mapWritableToInput(value) }
相關文章
- Spark(16) -- 資料讀取與儲存的主要方式Spark
- Python爬蟲學習筆記(三、儲存資料)Python爬蟲筆記
- 讀書筆記5-資料儲存篇筆記
- 學習筆記14:模型儲存筆記模型
- [PyTorch 學習筆記] 7.1 模型儲存與載入PyTorch筆記模型
- spark學習筆記--Spark調優與除錯Spark筆記除錯
- spark學習筆記--Spark SQLSpark筆記SQL
- spark學習筆記-- Spark StreamingSpark筆記
- Python零基礎學習筆記(二)——資料的儲存Python筆記
- spark學習筆記Spark筆記
- GlusterFS分散式儲存學習筆記分散式筆記
- 重新學習Mysql資料庫3:Mysql儲存引擎與資料儲存原理MySql資料庫儲存引擎
- Spark學習筆記(三)-Spark StreamingSpark筆記
- Spark讀取MySQL資料SparkMySql
- Spark 儲存模組原始碼學習Spark原始碼
- spark學習筆記--RDDSpark筆記
- EntityFramework Core筆記:儲存資料(4)Framework筆記
- Spark讀取MongoDB資料的方法與優化SparkMongoDB優化
- spark讀取hbase的資料Spark
- Spark讀取elasticsearch資料指南SparkElasticsearch
- spark學習筆記--叢集執行SparkSpark筆記
- java讀取倒序儲存的int型資料Java
- Mybatis讀取和儲存json型別的資料MyBatisJSON型別
- Scikit-Learn 與 TensorFlow 機器學習實用指南學習筆記 3 —— 資料獲取與清洗機器學習筆記
- 【大資料】BigTable分散式資料儲存系統分散式資料庫 | 複習筆記大資料分散式資料庫筆記
- 大神之路-起始篇 | 第3章.電腦科學導論之【資料儲存】學習筆記筆記
- 【numpy學習筆記】陣列的儲存和下載筆記陣列
- opencv學習筆記(二)-- 載入、修改和儲存影像OpenCV筆記
- 《資料資產管理核心技術與應用》讀書筆記-第二章:後設資料的採集與儲存筆記
- spark學習筆記--RDD鍵對操作Spark筆記
- java+pgsql實現儲存圖片到資料庫,以及讀取資料庫儲存的圖片JavaSQL資料庫
- 資料庫學習筆記資料庫筆記
- 《Python入門與資料科學庫》學習筆記Python資料科學筆記
- c++ (2-0) 從txt讀取和儲存資料C++
- C++(2) 從yml或者txt讀取和儲存資料C++
- 【學習筆記之作業系統原理篇】儲存管理筆記作業系統
- Redis資料儲存和讀寫Redis
- 讀取和儲存Excel表Excel