前言
Spark是基於Hadoop生態圈二次構建的,它支持多種輸入輸出源,spark可以通過Hadoop MapReduce 所使用的InputFormat 和 OutPutFormat 接口訪問數據,而大部分常見的文件格式與存儲系統(S3, HDFS, Cassandra, HBase 等)都支持這種接口。
數據讀取與存儲在spark 2.x之前是通過sqlContext/hiveContext進行讀取和寫入的,在spark 2.0以後可以使用SparkSession成員變量read來獲取相應的DataFrameReader進行讀取外界數據源,以及dataset的write成員變量來獲取DataFrameWriter,進行數據的寫入操作。
概述
DataFrameReader讀取數據,數據源可以選擇json/parquet/text/orc/csv等,一般情況下都有兩種方法去讀取:
// 指定format shcema option和路徑
sparkSession.read.
format("json/parquet/text/orc/csv").
schema(xx).
option("", xx).
load(path*)
// 利用不同的類型的特定方法
sparkSession.read.
json(path*)/csv(path*)/parquet(path*)/
orc(path*)/text(path*)/textFile(path*)
// jdbc單獨
DataFrameWriter用來寫入數據,寫入方式可以選擇json/parquet/text/orc/csv等,一般情況下都有兩種方法去寫入:
// 獲取方式
dataset/dataframe.write
def write: DataFrameWriter[T]
// 寫入文件
// Mode: SaveMode.Overwrite/SaveMode.Append/SaveMode.Ignore/SaveMode.ErrorIfExists
// Modestring: overwrite/append/ignore/error<errorifexists>
ds.write.
format("json/parquet/text/orc/csv").
mode("").
option(xx, xx).
save(path)
ds.write.
mode("").
json(path)/csv(path)/parquet(path)/
orc(path)/text(path)/textFile(path)
// 寫入表中:saveAsTable會根據column名字查找正確的列位置,
// insertInto會忽略位置,按照dataFrame的定義順序插入
def insertInto(tableName: String)
def saveAsTable(tableName: String)
// jdbc單獨
json
讀取json文件,DataFrameReader會自動推斷schema,先遍歷一遍數據集,抽取出schema,然後進行每行解析,所以對於各行如果存在模式不統一的情況,有的缺少某個字段,spark也會進行處理,相應的column設置爲None, 讀取後得到DataFrame,每一行schema爲json的推斷字段&類型,array裏面嵌套的jsonObj會被解析爲struct。
// 數據
{"name":"Michael"}
{"name":"Andy", "age":30}
{"name":"Justin", "age":19}
// 讀取
scala> var jsonDf = spark.read.format("json").load("/user/datasource/json/")
jsonDf: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
scala> var jsonDf = spark.read.json("/user/datasource/json/")
jsonDf: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
scala> jsonDf.printSchema
root
|-- age: long (nullable = true)
|-- name: string (nullable = true)
// 寫入
scala> jsonDf.write.format("csv").mode("overwrite").save("/user/datasource/json")
scala> jsonDf.write.mode("overwrite").json("/user/datasource/csv")
parquet
parquet是默認的讀取格式,是一種支持嵌套結構的存儲格式,並且使用了列式存儲的方式提升查詢性能。有兩種寫入讀取方式:
// 讀取
scala> val parquetDf = spark.read.format("parquet").load("/user/datasource/parquet")
parquetDf: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
scala> val parquetDf = spark.read.parquet("/user/datasource/parquet")
parquetDf: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
scala> val parquetDf = spark.read.load("/user/datasource/parquet")
parquetDf: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
// 寫入
scala> parquetDf.write.format("parquet").mode("overwrite").save("/user/datasource/parquet")
scala> parquetDf.write.mode("overwrite").parquet("/user/datasource/parquet")
scala> parquetDf.write.mode("overwrite").save("/user/datasource/parquet")
text
text格式的文件要求寫入一行只有一個字段,讀取時候讀取到的也是一個字段value,然後自行進行處理分析.
scala> val textDf = spark.read.text("/user/datasource/text")
textDf: org.apache.spark.sql.DataFrame = [value: string]
scala> val textDf = spark.read.format("text").load("/user/datasource/text")
textDf: org.apache.spark.sql.DataFrame = [value: string]
scala>df.write.mode("overwrite").format("text").save("/user/datasource/text")
scala>df.write.mode("overwrite").text("/user/datasource/text")
csv
csv數據是使用分隔符分割一條數據中的各字段,讀取實例,如果沒有指定表頭,spark會自動構建字段,依次爲_c0,_c1…;指定表頭會識別字段名字。
// 數據
,Michael
30,Andy
19,Justin
// 指定分隔符
option("sep"/"delimiter", "xx") 默認爲,
// 讀取無表頭,會自動按照,進行切分得到_c0, _c1
scala> var csvDf = spark.read.format("csv").load("/user/datasource/csv")
csvDf: org.apache.spark.sql.DataFrame = [_c0: string, _c1: string]
scala> csvDf.printSchema
root
|-- _c0: string (nullable = true)
|-- _c1: string (nullable = true)
// 寫入有表頭
scala> val namedCsvDf = csvDf.toDF(Seq("name", "age"): _*)
namedCsvDf: org.apache.spark.sql.DataFrame = [name: string, age: string]
scala> namedCsvDf.printSchema
root
|-- name: string (nullable = true)
|-- age: string (nullable = true)
scala> namedCsvDf.cache.count
res9: Long = 3
scala> namedCsvDf.write.format("csv").mode("overwrite").option("header", true).save("/user/bigdata/data_udd/tmp/lidongmeng/datasource/csv")
// 讀取有表頭
scala> var csvDf = spark.read.option("header", true).csv("/user/datasource/csv")
csvDf: org.apache.spark.sql.DataFrame = [name: string, age: string]
scala> val csvDf = spark.read.format("csv").option("header", true).load("/user/datasource/csv")
csvDf: org.apache.spark.sql.DataFrame = [name: string, age: string]
scala> csvDf.printSchema
root
|-- name: string (nullable = true)
|-- age: string (nullable = true)
jdbc
Jdbc來讀取和寫入關係型數據庫中的表,在進行hive<->mysql數據同步時候或者流式寫入mysql中會使用,同樣也有兩種方式寫入讀取:
// 讀取
val jdbcDF = spark.read
.format("jdbc")
.option("url", mysqlUrl)
.option("dbtable", "db.tablename")
.option("user", "username")
.option("password", "pwd")
.load()
val connectionProperties = new Properties()
connectionProperties.put("user", "username")
connectionProperties.put("password", "pwd")
val jdbcDF2 = spark.read
.jdbc(url, "db.tablename", connectionProperties)
// 寫入
jdbcDF.write
.format("jdbc")
.option("url", mysqlUrl)
.option("dbtable", "db.tablename")
.option("user", "username")
.option("password", "pwd")
.option("createTableColumnTypes", "xx varchar(60), xx varchar(60)") // 沒有指定的字段使用默認的類型
.save()
jdbcDF2.write
.jdbc(mysqlUrl, "db.tableName", connectionProperties)
參考
- https://blog.csdn.net/snail_gesture/article/details/51178666
- https://www.cnblogs.com/ywjy/p/7747482.html#csv