實戰spark 2.x讀取&存儲數據

前言

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)

參考

  1. https://blog.csdn.net/snail_gesture/article/details/51178666
  2. https://www.cnblogs.com/ywjy/p/7747482.html#csv
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章