spark對各種數據源的操作

RDD操作

文件類型

可能是本地或者hdfs,類型可能是jsom,text等
spark提供了讀取和保存saveas兩種類型api

sc.textFile("hdfs://hadoop102:9000/fruit.txt")
hdfsFile.saveAsTextFile("/fruitOut")

Sequence文件

SequenceFile文件是Hadoop用來存儲二進制形式的key-value對而設計的一種平面文件(Flat File)
SequenceFile文件只針對PairRDD,也就是k-v=結構

val seq = sc.sequenceFile[Int,Int]("file:///opt/module/spark/seqFile")
rdd.saveAsSequenceFile("file:///opt/module/spark/seqFile")

對象文件

對象保存要可以序列化

rdd.saveAsObjectFile("file:///opt/module/spark/objectFile")

讀取時指定類型,裏面的元素都是People

val objFile = sc.objectFile[People]("file:///opt/module/spark/objectFile")

MySQL數據庫

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.27</version>
</dependency>

讀取

import java.sql.DriverManager

import org.apache.spark.rdd.JdbcRDD
import org.apache.spark.{SparkConf, SparkContext}

object MysqlRDD {

 def main(args: Array[String]): Unit = {

   //1.創建spark配置信息
   val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("JdbcRDD")

   //2.創建SparkContext
   val sc = new SparkContext(sparkConf)

   //3.定義連接mysql的參數
   val driver = "com.mysql.jdbc.Driver"
   val url = "jdbc:mysql://hadoop102:3306/rdd"
   val userName = "root"
   val passWd = "000000"

   //創建JdbcRDD
   val rdd = new JdbcRDD(sc, () => {
     Class.forName(driver)
     DriverManager.getConnection(url, userName, passWd)
   },
     "select * from `rddtable` where `id`>=?;",
     1,
     10,
     1,
     r => (r.getInt(1), r.getString(2))
   )

   //打印最後結果
   println(rdd.count())
   rdd.foreach(println)

   sc.stop()
 }
}

寫入

def main(args: Array[String]) {
  val sparkConf = new SparkConf().setMaster("local[2]").setAppName("HBaseApp")
  val sc = new SparkContext(sparkConf)
  val data = sc.parallelize(List("Female", "Male","Female"))

  data.foreachPartition(insertData)
}

def insertData(iterator: Iterator[String]): Unit = {
Class.forName ("com.mysql.jdbc.Driver").newInstance()
  val conn = java.sql.DriverManager.getConnection("jdbc:mysql://hadoop102:3306/rdd", "root", "000000")
  iterator.foreach(data => {
    val ps = conn.prepareStatement("insert into rddtable(name) values (?)")
    ps.setString(1, data) 
    ps.executeUpdate()
  })
}

habse數據庫

由於 org.apache.hadoop.hbase.mapreduce.TableInputFormat 類的實現,Spark 可以通過Hadoop輸入格式訪問HBase。這個輸入格式會返回鍵值對數據,其中鍵的類型爲org. apache.hadoop.hbase.io.ImmutableBytesWritable,而值的類型爲org.apache.hadoop.hbase.client.
Result。

<dependency>
	<groupId>org.apache.hbase</groupId>
	<artifactId>hbase-server</artifactId>
	<version>1.3.1</version>
</dependency>

<dependency>
	<groupId>org.apache.hbase</groupId>
	<artifactId>hbase-client</artifactId>
	<version>1.3.1</version>
</dependency>

讀取

import org.apache.hadoop.conf.Configuration
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
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.hadoop.hbase.util.Bytes

object HBaseSpark {

  def main(args: Array[String]): Unit = {

    //創建spark配置信息
    val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("JdbcRDD")

    //創建SparkContext
    val sc = new SparkContext(sparkConf)

    //構建HBase配置信息
    val conf: Configuration = HBaseConfiguration.create()
    conf.set("hbase.zookeeper.quorum", "hadoop102,hadoop103,hadoop104")
    conf.set(TableInputFormat.INPUT_TABLE, "rddtable")

    //從HBase讀取數據形成RDD
    val hbaseRDD: RDD[(ImmutableBytesWritable, Result)] = sc.newAPIHadoopRDD(
      conf,
      classOf[TableInputFormat],
      classOf[ImmutableBytesWritable],
      classOf[Result])

    val count: Long = hbaseRDD.count()
    println(count)

    //對hbaseRDD進行處理
    hbaseRDD.foreach {
      case (_, result) =>
        val key: String = Bytes.toString(result.getRow)
        val name: String = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")))
        val color: String = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("color")))
        println("RowKey:" + key + ",Name:" + name + ",Color:" + color)
    }

    //關閉連接
    sc.stop()
  }
}

寫入

def main(args: Array[String]) {
//獲取Spark配置信息並創建與spark的連接
  val sparkConf = new SparkConf().setMaster("local[*]").setAppName("HBaseApp")
  val sc = new SparkContext(sparkConf)

//創建HBaseConf
  val conf = HBaseConfiguration.create()
  val jobConf = new JobConf(conf)
  jobConf.setOutputFormat(classOf[TableOutputFormat])
  jobConf.set(TableOutputFormat.OUTPUT_TABLE, "fruit_spark")

//構建Hbase表描述器
  val fruitTable = TableName.valueOf("fruit_spark")
  val tableDescr = new HTableDescriptor(fruitTable)
  tableDescr.addFamily(new HColumnDescriptor("info".getBytes))

//創建Hbase表
  val admin = new HBaseAdmin(conf)
  if (admin.tableExists(fruitTable)) {
    admin.disableTable(fruitTable)
    admin.deleteTable(fruitTable)
  }
  admin.createTable(tableDescr)

//定義往Hbase插入數據的方法
  def convert(triple: (Int, String, Int)) = {
    val put = new Put(Bytes.toBytes(triple._1))
    put.addImmutable(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes(triple._2))
    put.addImmutable(Bytes.toBytes("info"), Bytes.toBytes("price"), Bytes.toBytes(triple._3))
    (new ImmutableBytesWritable, put)
  }

//創建一個RDD
  val initialRDD = sc.parallelize(List((1,"apple",11), (2,"banana",12), (3,"pear",13)))

//將RDD內容寫到HBase
  val localData = initialRDD.map(convert)

  localData.saveAsHadoopDataset(jobConf)

sparksql操作

  1. Spark SQL的默認數據源爲Parquet格式 數據源爲Parquet文件時,Spark SQL可以方便的執行所有的操作。修改配置項spark.sql.sources.default,可修改默認數據源格式當數據源格式
  2. 不是parquet格式文件時,需要手動指定數據源的格式。數據源格式需要指定全名(例如:org.apache.spark.sql.parquet),如果數據源格式爲內置格式,則只需要指定簡稱定json, parquet, jdbc, orc, libsvm, csv, text來指定數據的格式
  3. 可以通過SparkSession提供的read.load方法用於通用加載數據,使用write和save保存數據
  4. 可以直接運行SQL在文件上

寫數據的4個選項

Scala/Java Any Language Meaning
SaveMode.ErrorIfExists(default) “error”(default) 如果文件存在,則報錯
SaveMode.Append “append” 追加
SaveMode.Overwrite “overwrite” 覆寫
SaveMode.Ignore “ignore” 數據存在,則忽略

JSON文件

Spark SQL 能夠自動推測 JSON數據集的結構,並將它加載爲一個Dataset[Row]. 可以通過SparkSession.read.json()去加載一個 一個JSON 文件

import spark.implicits._

val path = "examples/src/main/resources/people.json"
val peopleDF = spark.read.json(path)

peopleDF.createOrReplaceTempView("people")

Parquet文件

Parquet是一種流行的列式存儲格式,可以高效地存儲具有嵌套字段的記錄。Parquet格式經常在Hadoop生態圈中被使用,它也支持Spark SQL的全部數據類型

importing spark.implicits._
import spark.implicits._

val peopleDF = spark.read.json("examples/src/main/resources/people.json")

peopleDF.write.parquet("hdfs://hadoop102:9000/people.parquet")

val parquetFileDF = spark.read.parquet("hdfs:// hadoop102:9000/people.parquet")

parquetFileDF.createOrReplaceTempView("parquetFile")

JDBC

Spark SQL可以通過JDBC從關係型數據庫中讀取數據的方式創建DataFrame,通過對DataFrame一系列的計算後,還可以將數據再寫回關係型數據庫中
讀數據

val connectionProperties = new Properties()
connectionProperties.put("user", "root")
connectionProperties.put("password", "000000")
val jdbcDF2 = spark.read
.jdbc("jdbc:mysql://hadoop102:3306/db_test", "rddtable", connectionProperties)

寫數據

jdbcDF.write.jdbc("jdbc:mysql://hadoop102:3306/rdd", "db", connectionProperties)

Hive數據庫

Apache Hive是Hadoop上的SQL引擎,Spark SQL編譯時可以包含Hive支持,也可以不包含。包含Hive支持的Spark SQL可以支持Hive表訪問、UDF(用戶自定義函數)以及 Hive 查詢語言(HiveQL/HQL)等。需要強調的一點是,如果要在Spark SQL中包含Hive的庫,並不需要事先安裝Hive。一般來說,最好還是在編譯Spark SQL時引入Hive支持,這樣就可以使用這些特性了。如果你下載的是二進制版本的 Spark,它應該已經在編譯時添加了 Hive 支持。
若要把Spark SQL連接到一個部署好的Hive上,你必須把hive-site.xml複製到 Spark的配置文件目錄中($SPARK_HOME/conf)。即使沒有部署好Hive,Spark SQL也可以運行。 需要注意的是,如果你沒有部署好Hive,Spark SQL會在當前的工作目錄中創建出自己的Hive 元數據倉庫,叫作 metastore_db。此外,如果你嘗試使用 HiveQL 中的 CREATE TABLE (並非 CREATE EXTERNAL TABLE)語句來創建表,這些表會被放在你默認的文件系統中的 /user/hive/warehouse 目錄中(如果你的 classpath 中有配好的 hdfs-site.xml,默認的文件系統就是 HDFS,否則就是本地文件系統)。

內嵌Hive應用

如果要使用內嵌的Hive,什麼都不用做,直接用就可以了。
可以通過添加參數初次指定數據倉庫地址:

--conf spark.sql.warehouse.dir=hdfs://hadoop102/spark-wearhouse

注意:如果你使用的是內部的Hive,在Spark2.0之後,spark.sql.warehouse.dir用於指定數據倉庫的地址,如果你需要是用HDFS作爲路徑,那麼需要將core-site.xml和hdfs-site.xml 加入到Spark conf目錄,否則只會創建master節點上的warehouse目錄,查詢時會出現文件找不到的問題,這是需要使用HDFS,則需要將metastore刪除,重啓集羣。

外部Hive應用

如果想連接外部已經部署好的Hive,需要通過以下幾個步驟。

  1. 將Hive中的hive-site.xml拷貝或者軟連接到Spark安裝目錄下的conf目錄下。
  2. 打開spark shell,注意帶上訪問Hive元數據庫的JDBC客戶端
$ bin/spark-shell  --jars mysql-connector-java-5.1.27-bin.jar

運行Spark SQL CLI

Spark SQL CLI可以很方便的在本地運行Hive元數據服務以及從命令行執行查詢任務。在Spark目錄下執行如下命令啓動Spark SQL CLI:

./bin/spark-sql

代碼中使用Hive

(1)添加依賴:

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-hive_2.11</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>org.apache.hive</groupId>
    <artifactId>hive-exec</artifactId>
    <version>1.2.1</version>
</dependency>

(2)創建SparkSession時需要添加hive支持(紅色部分)

val warehouseLocation: String = new File("spark-warehouse").getAbsolutePath

val spark = SparkSession
.builder()
.appName("Spark Hive Example")
.config("spark.sql.warehouse.dir", warehouseLocation)
.enableHiveSupport()
.getOrCreate()

注意:藍色部分爲使用內置Hive需要指定一個Hive倉庫地址。若使用的是外部Hive,則需要將hive-site.xml添加到ClassPath下。

sparkstreaming操作

文件數據源

  1. textFileStream會監控dataDirectory目錄並不斷處理移動進來的文件,記住目前不支持嵌套目錄
  2. 文件需要有相同的數據格式;
  3. 文件進入 dataDirectory的方式需要通過移動或者重命名來實現
  4. 一旦文件移動進目錄,則不能再修改,即便修改了也不會讀取新數據;
    val ssc = new StreamingContext(sparkConf, Seconds(5))
    val dirStream = ssc.textFileStream("hdfs://hadoop102:9000/fileStream")

啓動程序並向fileStream目錄上傳文件

[atguigu@hadoop102 data]$ hadoop fs -put ./a.tsv /fileStream
[atguigu@hadoop102 data]$ hadoop fs -put ./b.tsv /fileStream

自定義數據源

import java.io.{BufferedReader, InputStreamReader}
import java.net.Socket
import java.nio.charset.StandardCharsets

import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.receiver.Receiver

class CustomerReceiver(host: String, port: Int) extends Receiver[String](StorageLevel.MEMORY_ONLY) {

  //最初啓動的時候,調用該方法,作用爲:讀數據並將數據發送給Spark
  override def onStart(): Unit = {
    new Thread("Socket Receiver") {
      override def run() {
        receive()
      }
    }.start()
  }

  //讀數據並將數據發送給Spark
  def receive(): Unit = {

    //創建一個Socket
    var socket: Socket = new Socket(host, port)

    //定義一個變量,用來接收端口傳過來的數據
    var input: String = null

    //創建一個BufferedReader用於讀取端口傳來的數據
    val reader = new BufferedReader(new InputStreamReader(socket.getInputStream, StandardCharsets.UTF_8))

    //讀取數據
    input = reader.readLine()

    //當receiver沒有關閉並且輸入數據不爲空,則循環發送數據給Spark
    while (!isStopped() && input != null) {
      store(input)
      input = reader.readLine()
    }

    //跳出循環則關閉資源
    reader.close()
    socket.close()

    //重啓任務
    restart("restart")
  }

  override def onStop(): Unit = {}
}

使用自定義的數據源採集數據

   val ssc = new StreamingContext(sparkConf, Seconds(5))
   val lineStream = ssc.receiverStream(new CustomerReceiver("hadoop102", 9999))

Kafka數據源

Spark消費Kafka的兩種方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章