spark和RDD的知識梳理與總結

什麼是Spark

基於內存的,用於大規模數據處理(離線計算、實時計算、快速查詢(交互式查詢))的統一分析引擎。

 

Spark特點

快: 

Spark計算速度是MapReduce計算速度的10-100倍

易用:(算法多)

MR支持1種計算模型,Spsark支持更多的計算模型。

通用:

Spark 能夠進行離線計算、交互式查詢(快速查詢)、實時計算、機器學習、圖計算等

兼容性:

Spark支持大數據中的Yarn調度,支持mesos。可以處理hadoop計算的數據。

 

Spark發展史

1、2009年誕生於美國加州大學伯克利分校AMP 實驗室

2、2014年,Spark 成爲 Apache 的頂級項目

 

Spark爲什麼會流行

原因1:優秀的數據模型和計算抽象

Spark將多個RDD之間的中間數據寫入內存,MR將中間數據寫入硬盤,內存比硬盤速度快

原因2:完善的生態圈

Spark 能夠進行離線計算、交互式查詢(快速查詢)、實時計算、機器學習、圖計算等

Spark Core:Spark基本功能

Spark SQL : 操作結構化數據

Spark Streaming: 對實時數據進行流式計算

Spark MLlib: 機器學習

GraphX:用於圖計算

 

Hadoop與Spark的對比

 

 

Hadoop

Spark

類型

基礎平臺, 包含計算, 存儲, 調度

分佈式計算工具

場景

大規模數據集上的批處理

迭代計算, 交互式計算, 流計算

價格

對機器要求低, 便宜

對內存有要求, 相對較貴

編程範式

Map+Reduce, API 較爲底層, 算法適應性差

RDD組成DAG有向無環圖, API 較爲頂層, 方便使用

數據存儲結構

MapReduce中間計算結果存在HDFS磁盤上, 延遲大

RDD中間運算結果存在內存中 , 延遲小

運行方式

Task以進程方式維護, 任務啓動慢

Task以線程方式維護, 任務啓動快

 

 

Spark運行模式

1.local本地模式(單機)--開發測試使用

2.standalone獨立集羣模式--開發測試使用

3.standalone-HA高可用模式--生產環境使用

4.on yarn集羣模式--生產環境使用

5.on mesos集羣模式--國內使用較少

6.on cloud集羣模式--中小公司未來會更多的使用雲服務

 

Spark安裝部署

1、local本地模式安裝部署

第一步:上傳並解壓

第二步:開箱即用(不需要修改配置)

啓動spark

./spark-shell  

沒有參數表示   等於  local[*]

./spark-shell  --master local[*]

Local表示在本機運行。[*]表示可用的所有資源

./spark-shell  --master local[n]

Local表示在本機運行。[n]表示使用n個線程

 

Spark初體驗

1、讀取本地數據計算WordCount

val textFile = sc.textFile("file:///opt/tt.txt")

val counts = textFile.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)

2、讀取hdfs集羣數據計算WordCount

  val textFile = sc.textFile("hdfs://node01:8020/tt.txt")

val counts = textFile.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)

counts.saveAsTextFile("hdfs://node01:8020/ttt")

 

standalone集羣模式

第一步:軟件上傳並解壓

第二步:

修改配置文件

export JAVA_HOME=${JAVA_HOME}

export SPARK_MASTER_HOST=node01

export SPARK_MASTER_PORT=7077

修改slaves

node02

node03

第三步:軟件包的分發

scp -r spark-2.2.0-bin-2.6.0-cdh5.14.0 node02:$PWD

scp -r spark-2.2.0-bin-2.6.0-cdh5.14.0 node03:$PWD

第四步:啓動集羣

./start-all.sh –master spark://node01:7077

第五步:驗證

sc.textFile("hdfs://node01:8020/tt.txt")

.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)

.saveAsTextFile("hdfs://node01:8020/tttt")

 

standalone-HA高可用模式(不用的話就不需要去配置)   

HA解決master的單點問題

第一步:上傳解壓

第二步:修改配置文件

export JAVA_HOME=${JAVA_HOME}

export SPARK_MASTER_PORT=7077

export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER  -Dspark.deploy.zooke

eper.url=node01:2181,node02:2181,node03:2181  -Dspark.deploy.zookeeper.dir=/spark"

"../conf/spark-env.sh" 67L, 3966C written

第三步:同步更新的配置到其他節點

scp spark-env.sh node02:/$PWD

scp spark-env.sh node03:/$PWD

第四步:啓動zookeeper

第五步:啓動spark

啓動集羣  ./start-all.sh

啓動第二個Master(node02/node03)

第六步:驗證HA

http://node01:8080/

http://node02:8080/

on yarn集羣模式

前期準備

  1. hadoop安裝部署好

     2、安裝部署單機版本的spark

第一步:安裝包的上傳並解壓

第二步:修改配置文件

export JAVA_HOME=${JAVA_HOME}

export SPARK_MASTER_HOST=node01

export SPARK_MASTER_PORT=7077

export HADOOP_CONF_DIR=/export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop

第三步測試

使用spark-submits

/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/bin/spark-submit \

--class org.apache.spark.examples.SparkPi \

--master yarn \

--deploy-mode cluster \

--driver-memory 1g \

--executor-memory 1g \

--executor-cores 2 \

--queue default \

/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/examples/jars/spark-examples_2.11-2.2.0.jar \

10

參數的說明

--master  local [n]/[*]/spark://HOST:PORT/YARN   指定 Master 的地址

--name "appName"                 指定程序運行的名稱

--class                            程序的main方法所在的類

--jars  xx.jar                       程序額外使用的 jar 包

--driver-memory  512m               Driver運行所需要的內存, 默認1g

--executor-memory  2g               指定每個 executor 可用內存爲 2g, 默認1g

--executor-cores  1                  指定每一個 executor 可用的核數

--total-executor-cores  2              指定整個集羣運行任務使用的 cup 核數爲 2 個

 --queue default    指定任務的對列

--deploy-mode 指定運行模式(client/cluster)

Spark編寫代碼

  1. 創建一個 Sparkconf對象,設置app名稱
  2. 創建一個SparkContext,
  3. 讀取數據,對數據進行計算
  4. 保存數據

將代碼導出成爲jar文件,將jar文件添加到集羣,使用sparkSubmit提交任務到集羣。

 

 

SparkCore

 

什麼是RDD

彈性分佈式數據集(數據存儲在內存)

彈性的,RDD中的數據可以保存在內存中或者磁盤裏面

分佈式存儲的,可以用於分佈式計算

           集合,可以存放很多元素

一個不可變、可分區、裏面的元素可並行計算的集合 

RDD的主要屬性

1、數據集的基本組成單位是一組分片(Partition)或一個分區(Partition)列表

每個分片都會被一個計算任務處理,分片數決定並行度。

用戶可以在創建RDD時指定RDD的分片個數,如果沒有指定,那麼就會採用默認值(默認值2)

 

2、一個函數會被作用在每一個分區。

   Spark中RDD的計算是以分區爲單位的,函數會被作用到每個分區上

 

3、一個RDD會依賴於其他多個RDD。

RDD的每次轉換都會生成一個新的RDD,所以RDD之間就會形成類似於流水線一樣的前後依賴關係在部分分區數據丟失時,Spark可以通過這個依賴關係重新計算丟失的分區數據,而不是對RDD的所有分區進行重新計算。(Spark的容錯機制)

 

4.KV類型的RDD會有一個Partitioner函數

非key-value的RDD的Parititioner的值是None,Partitioner函數決定了RDD本身的分區數量,也決定了parent RDD Shuffle輸出時的分區數量。

 

5、每個RDD維護一個列表,每個Partition的位置(preferred location)存儲在一個列表中。

 

RDD-API

創建RDD

1、由外部存儲系統的數據集創建

val rdd1 = sc.textFile("hdfs://node01:8020/wordcount/input/words.txt")

2、通過已有的RDD經過算子轉換生成新的RDD

val rdd2=rdd1.flatMap(_.split(" "))

3、由一個已經存在的Scala集合創建

方法1   sc.parallelize(Array(1,2,3,4,5,6,7,8))

方法2   sc.makeRDD(List(1,2,3,4,5,6,7,8))

makeRDD方法底層調用了parallelize方法

 

RDD方法/算子分類

RDD的算子分爲兩類:

1.Transformation轉換操作:返回一個新的RDD

2.Action動作操作:返回值不是RDD(無返回值或返回其他的)

問答題:判斷一個算子是Transformation??還是Action??

若返回值的類型是一個RDD那麼這個算子就是Transformation,反之就是Action

 

如何理解spark的惰性計算?

RDD中的所有轉換都是惰性求值/延遲執行的,也就是說並不會直接計算

遇到Action動作時,這些轉換纔會真正運行

 

之所以使用惰性求值/延遲執行,是因爲這樣可以在Action時對RDD操作形成DAG有向無環圖進行Stage的劃分和並行優化,這種設計讓Spark更加有效率地運行。

 

Transformation轉換算子

轉換

含義

map(func)

返回一個新的RDD,該RDD由每一個輸入元素經過func函數轉換後組成

filter(func)

返回一個新的RDD,該RDD由經過func函數計算後返回值爲true的輸入元素組成

flatMap(func)

類似於map,但是每一個輸入元素可以被映射爲0或多個輸出元素(所以func應該返回一個序列,而不是單一元素)

mapPartitions(func)

類似於map,但獨立地在RDD的每一個分片上運行,因此在類型爲T的RDD上運行時,func的函數類型必須是Iterator[T] => Iterator[U]

mapPartitionsWithIndex(func)

類似於mapPartitions,但func帶有一個整數參數表示分片的索引值,因此在類型爲T的RDD上運行時,func的函數類型必須是

(Int, Interator[T]) => Iterator[U]

sample(withReplacement, fraction, seed)

根據fraction指定的比例對數據進行採樣,可以選擇是否使用隨機數進行替換,seed用於指定隨機數生成器種子

union(otherDataset)

對源RDD和參數RDD求並集後返回一個新的RDD

intersection(otherDataset)

對源RDD和參數RDD求交集後返回一個新的RDD

distinct([numTasks]))

對源RDD進行去重後返回一個新的RDD

groupByKey([numTasks])   

在一個(K,V)的RDD上調用,返回一個(K, Iterator[V])的RDD

reduceByKey(func, [numTasks])

在一個(K,V)的RDD上調用,返回一個(K,V)的RDD,使用指定的reduce函數,將相同key的值聚合到一起,與groupByKey類似,reduce任務的個數可以通過第二個可選的參數來設置

aggregateByKey(zeroValue)(seqOp, combOp, [numTasks])

 

sortByKey([ascending], [numTasks])

在一個(K,V)的RDD上調用,K必須實現Ordered接口,返回一個按照key進行排序的(K,V)的RDD

sortBy(func,[ascending], [numTasks])

與sortByKey類似,但是更靈活

join(otherDataset, [numTasks])

在類型爲(K,V)和(K,W)的RDD上調用,返回一個相同key對應的所有元素對在一起的(K,(V,W))的RDD

cogroup(otherDataset, [numTasks])

在類型爲(K,V)和(K,W)的RDD上調用,返回一個(K,(Iterable<V>,Iterable<W>))類型的RDD

cartesian(otherDataset)

笛卡爾積

pipe(command, [envVars])

對rdd進行管道操作

coalesce(numPartitions)  

減少 RDD 的分區數到指定值。在過濾大量數據之後,可以執行此操作

repartition(numPartitions)

重新給 RDD 分區

Action算子

動作

含義

reduce(func)

通過func函數聚集RDD中的所有元素,這個功能必須是可交換且可並聯的

collect()

在驅動程序中,以數組的形式返回數據集的所有元素

count()

返回RDD的元素個數

first()

返回RDD的第一個元素(類似於take(1))

take(n)

返回一個由數據集的前n個元素組成的數組

takeSample(withReplacement,num, [seed])

返回一個數組,該數組由從數據集中隨機採樣的num個元素組成,可以選擇是否用隨機數替換不足的部分,seed用於指定隨機數生成器種子

takeOrdered(n, [ordering])

返回自然順序或者自定義順序的前 n 個元素

saveAsTextFile(path)

將數據集的元素以textfile的形式保存到HDFS文件系統或者其他支持的文件系統,對於每個元素,Spark將會調用toString方法,將它裝換爲文件中的文本

saveAsSequenceFile(path)

將數據集中的元素以Hadoop sequencefile的格式保存到指定的目錄下,可以使HDFS或者其他Hadoop支持的文件系統。

saveAsObjectFile(path)

將數據集的元素,以 Java 序列化的方式保存到指定的目錄下

countByKey()

針對(K,V)類型的RDD,返回一個(K,Int)的map,表示每一個key對應的元素個數。

foreach(func)

在數據集的每一個元素上,運行函數func進行更新。

foreachPartition(func)

在數據集的每一個分區上,運行函數func

 

統計操作

算子

含義

count

個數

mean

均值

sum

求和

max

最大值

min

最小值

variance

方差

sampleVariance

從採樣中計算方差

stdev

標準差:衡量數據的離散程度

sampleStdev

採樣的標準差

stats

查看統計結果

 

repartition可以增加和減少rdd中的分區數,

coalesce默認減少rdd分區數,增加rdd分區數不會生效。

不管增加還是減少分區數原rdd分區數不變,變的是新生成的rdd的分區數

分區原則

1.啓動的時候指定的CPU核數確定了一個參數值:

spark.default.parallelism=指定的CPU核數(集羣模式最小2)

2.對於Scala集合調用parallelize(集合,分區數)方法,

如果沒有指定分區數,就使用spark.default.parallelism,

如果指定了就使用指定的分區數(不要指定大於spark.default.parallelism)

3.對於textFile(文件,分區數)     defaultMinPartitions

如果沒有指定分區數sc.defaultMinPartitions=min(defaultParallelism,2)

如果指定了就使用指定的分區數sc.defaultMinPartitions=指定的分區數

rdd的分區數

對於本地文件:

rdd的分區數 = max(本地file的分片數, sc.defaultMinPartitions)

對於HDFS文件:

rdd的分區數 = max(hdfs文件的block數目, sc.defaultMinPartitions)

所以如果分配的核數爲多個,且從文件中讀取數據創建RDD,即使hdfs文件只有1個切片,最後的Spark的RDD的partition數也有可能是2

 

rdd數據持久化

redis數據持久化什麼作用?(將內存中的數據寫入到硬盤中,進行永久保存)

防止數據丟失!

 

Rdd數據持久化什麼作用?

1、對多次使用的rdd進行緩存,緩存到內存,當後續頻繁使用時直接在內存中讀取緩存的數據,不需要重新計算。

2、將RDD結果寫入硬盤(容錯機制),當RDD丟失數據時,或依賴的RDD丟失數據時,可以使用持久化到硬盤的數據恢復。

 

持久化/緩存(內存)

緩存方法

Persist

Cache
這兩個方法被調用時立即緩存,而是觸發後面的action時

代碼

val rdd1 = sc.textFile("hdfs://node01:8020/wordcount/input/words.txt")

val rdd2 = rdd1.flatMap(x=>x.split(" ")).map((_,1)).reduceByKey(_+_)

rdd2.cache

 

rdd2.sortBy(_._2,false).collect//觸發action,會去讀取HDFS的文件,rdd2會真正執行持久化

rdd2.sortBy(_._2,false).collect

 

緩存級別

掌握的級別

MEMORY_ONLY(默認):數據寫入內存,內存不足,部分分區數據不緩存。

MEMORY_AND_DISK數據寫入內存,內存不足寫入磁盤。

DISK_ONLY數據寫入硬盤。

總結:

1.RDD持久化/緩存的目的是爲了提高後續操作的速度

2.緩存的級別有很多,默認只存在內存中,開發中使用memory_and_disk

3.只有執行action操作的時候纔會真正將RDD數據進行持久化/緩存

4.實際開發中如果某一個RDD後續會被頻繁的使用,可以將該RDD進行持久化/緩存

 

RDD持久化(容錯機制Checkpoint

將數據寫入HDFS,利用HDFS永久存儲。

操作過程

  1. 設置持久化的存儲路徑
  2. 調用checkpoint()進行數據的保存

SparkContext.setCheckpointDir("目錄") //HDFS的目錄

RDD.checkpoint()

 

代碼:

sc.setCheckpointDir("hdfs://node01:8020/ckpdir")  

val rdd1 = sc.textFile("hdfs://node01:8020/wordcount/input/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)

rdd1.checkpoint()

rdd1.collect

持久化結果

 

cache和Checkpoint的區別

位置

Persist 和 Cache將數據保存在內存

Checkpoint將數據保存在HDFS

生命週期

Persist 和 Cache  程序結束後會被清除或手動調用unpersist方法。

Checkpoint永久存儲不會被刪除。

RDD依賴關係(血統Lineage)

Persist和Cache,不會丟掉RDD間的依賴鏈/依賴關係

Checkpoint會斬斷依賴鏈

 

 

 

RDD的依賴關係

窄依賴:父RDD的一個分區只會被子RDD的一個分區依賴(圖一)

寬依賴:父RDD的一個分區會被子RDD的多個分區依賴(涉及到shuffle)(圖二)

爲什麼劃分寬窄依賴?

1.對於寬依賴

是劃分Stage的依據(目的是實現並行化計算)

2.對於窄依賴

Spark  Stage可以並行計算(並行計算速度快)

 

 DAG介紹

DAG:指的是數據轉換執行的過程,有方向,無閉環(其實就是RDD執行的流程)

DAG邊界

開始:通過SparkContext創建的RDD

結束:觸發Action,一旦觸發Action就形成了一個完整的DAG

說明:

一個Spark應用中可以有一到多個DAG,取決於觸發了多少次Action

 

一個DAG中會有不同的階段/stage,劃分階段/stage的依據就是寬依賴

一個階段/stage中可以有多個Task,一個分區對應一個Task

爲什麼要劃分Stage??

爲了實現並行化計算,提高計算效率。

 

總結

Spark會根據shuffle/寬依賴使用回溯算法來對DAG進行Stage劃分,從後往前,遇到寬依賴就斷開,遇到窄依賴就把當前的RDD加入到當前的stage/階段中

 

哪些算法是寬依賴??

??????

Spark概念介紹

 

1.Application:指的是用戶編寫的Spark應用程序/代碼,包含了Driver功能代碼和分佈在集羣中多個節點上運行的Executor代碼。

2.Driver:Spark中的Driver即運行上述Application的Main()函數並且創建SparkContext,SparkContext負責和ClusterManager通信,進行資源的申請、任務的分配和監控等

3.Cluster Manager:指的是在集羣上獲取資源的外部服務,Standalone模式下由Master負責,Yarn模式下ResourceManager負責;

4.Executor:是運行在工作節點Worker上的進程,負責運行任務,併爲應用程序存儲數據,是執行分區計算任務的進程;

5.RDD:Resilient Distributed Dataset彈性分佈式數據集,是分佈式內存的一個抽象概念;

6.DAG:Directed Acyclic Graph有向無環圖,反映RDD之間的依賴關係和執行流程;

7.Job:作業,按照DAG執行就是一個作業;Job==DAG

8.Stage:階段,是作業的基本調度單位,同一個Stage中的Task可以並行執行,多個Task組成TaskSet任務集

9.Task:任務,運行在Executor上的工作單元,一個Task計算一個分區,包括pipline上的一系列操作

 

Spark執行任務的基本流程(簡答-Yarn 缺)

 

1.當一個Spark應用被提交時,首先需要爲這個Spark Application構建基本的運行環境,即由任務控制節點(Driver)創建一個SparkContext,

2.SparkContext向資源管理器註冊並申請運行Executor資源;

3.資源管理器爲Executor分配資源並啓動Executor進程,Executor運行情況將隨着心跳發送到資源管理器上;

4.SparkContext根據RDD的依賴關係構建成DAG圖,並提交給DAGScheduler進行解析劃分成Stage,並把該Stage中的Task組成Taskset發送給TaskScheduler。

5.TaskScheduler將Task發放給Executor運行,同時SparkContext將應用程序代碼發放給Executor。

6.Executor將Task丟入到線程池中執行,把執行結果反饋給任務調度器,然後反饋給DAG調度器,運行完畢後寫入數據並釋放所有資源。

總結(master,缺Yarn)

1.Spark應用被提交-->SparkContext向資源管理器註冊並申請資源 (??) -->啓動Executor

2.RDD-->構建DAG-->DAGScheduler劃分Stage形成TaskSet-->TaskScheduler提交Task-->Worker上的Executor執行Task

RDD累加器和廣播變量

1.累加器accumulators:累加器支持在所有不同節點之間進行累加計算

 

 

 

package cn.itcast.core

import org.apache.spark.rdd.RDD
import org.apache.spark.{Accumulator, SparkConf, SparkContext}

object AccumulatorTest {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")

    //使用scala集合完成累加
    var counter1: Int = 0;
    var data = Seq(1,2,3)
    data.foreach(x => counter1 += x )
    println(counter1) 

 

 


    println("+++++++++++++++++++++++++")

    //使用RDD進行累加
    var counter2: Int = 0;
    val dataRDD: RDD[Int] = sc.parallelize(data) //分佈式集合的[1,2,3]
    dataRDD.foreach(x => counter2 += x)
    println(counter2)//0


    //注意:上面的RDD操作運行結果是0
    //因爲foreach中的函數是傳遞給Worker中的Executor執行,用到了counter2變量
    //counter2變量在Driver端定義的,在傳遞給Executor的時候,各個Executor都有了一份counter2
    //最後各個Executor將各自個x加到自己的counter2上面了,Driver端的counter2沒有關係

    //那這個問題得解決啊!不能因爲使用了Spark連累加都做不了了啊!
    //如果解決?---使用累加器
    val counter3: Accumulator[Int] = sc.accumulator(0)
    dataRDD.foreach(x => counter3 += x)
    println(counter3)//6
  }
}

2.廣播變量broadcast variables:廣播變量用來把變量在所有節點的內存之間進行共享,在每個機器上緩存一個只讀的變量,而不是爲機器上的每個任務都生成一個副本

import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object BroadcastVariablesTest {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")

    //不使用廣播變量
    val kvFruit: RDD[(Int, String)] = sc.parallelize(List((1,"apple"),(2,"orange"),(3,"banana"),(4,"grape")))
    val fruitMap: collection.Map[Int, String] =kvFruit.collectAsMap
    //scala.collection.Map[Int,String] = Map(2 -> orange, 4 -> grape, 1 -> apple, 3 -> banana)
    val fruitIds: RDD[Int] = sc.parallelize(List(2,4,1,3))
    //根據水果編號取水果名稱
    val fruitNames: RDD[String] = fruitIds.map(x=>fruitMap(x))
    fruitNames.foreach(println)
    //注意:以上代碼看似一點問題沒有,但是考慮到數據量如果較大,Task數較多,
    //那麼會導致,被各個Task共用到的fruitMap會被多次傳輸
    //應該要減少fruitMap的傳輸,一臺機器上一個,被該臺機器中的Task共用即可
    //如何做到?---使用廣播變量
    println("=====================")

 

//先將數據添加到廣播變量
    val BroadcastFruitMap: Broadcast[collection.Map[Int, String]] = sc.broadcast(fruitMap)

//在廣播變量中獲取數據
    val fruitNames2: RDD[String] = fruitIds.map(x=>BroadcastFruitMap.value(x))
    fruitNames2.foreach(println)
  }
}

通常在兩個表(一個大表一個小表,將小表廣播出去)的join時會用到廣播變量。

 

txt文本用sc將數據寫入到msql和從msql中讀取

package spark02

import java.sql.{Connection, Date, DriverManager, PreparedStatement}

import org.apache.spark.rdd.{JdbcRDD, RDD}
import org.apache.spark.{SparkConf, SparkContext}
object demo29 {
  def main(args: Array[String]): Unit = {
    //創建SparkContext
    val sc = new SparkContext(new SparkConf().setAppName("demo29").setMaster("local[*]"))
//    xie(sc)
    du(sc)
  }
  def xie (sc:SparkContext)={
    //讀取數據
    val date  = sc.textFile("D:\\dev\\dashuju\\spark\\第三天\\4.7號練習題\\user29.txt").map(x=>{var lien=x.split(" ");(List(lien(0),lien(1),lien(2),lien(3)))})
    val conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bigdata0407?characterEncoding=UTF-8", "root", "root")
    //遍歷循環
    date.toLocalIterator.foreach(date=>{
      //將每一條數據存入到MySQL
      //val一個sql語句
      val sql = "insert into `user` (id, username, birthday, sex, address) values (null, ? , ? , ? , ?);"
      val ps = conn.prepareStatement(sql)
      ps.setString(1,date(0))
      ps.setDate(2, Date.valueOf(date(1).replaceAll("/", "-")))
      ps.setString(3,date(2))
      ps.setString(4,date(3))
      // 數據提交
      ps.execute()
    })
    conn.close()
  }
  def du(sc:SparkContext)={
    def getConn():Connection={
      DriverManager.getConnection("jdbc:mysql://localhost:3306/bigdata0407?characterEncoding=UTF-8", "root", "root")
    }
    val studentRDD: JdbcRDD[(String, Date, String,String)] = new JdbcRDD(sc, getConn, "select * from user where id >= ? and id <= ? ",
      4, 6, 2,
      rs => {

        val username: String = rs.getString("username")
        val birthday: Date = rs.getDate("birthday")
        val sex: String = rs.getString("sex")
        val address: String = rs.getString("address")
        (username, birthday, sex,address)
        }
    )
    println(studentRDD.collect().toBuffer)
    getConn.close()
  }



}

txt文本用sc將數據寫入到hbase和從hbase中讀取

package spark02

import java.util.UUID

import org.apache.hadoop.hbase.HBaseConfiguration
import org.apache.hadoop.hbase.client.{Put, Result}
import org.apache.hadoop.hbase.io.ImmutableBytesWritable
import org.apache.hadoop.hbase.mapred.TableOutputFormat
import org.apache.hadoop.hbase.mapreduce.TableInputFormat
import org.apache.hadoop.hbase.util.Bytes
import org.apache.hadoop.mapred.JobConf
import org.apache.spark.{SparkConf, SparkContext}

 

object demo30 {
  def main(args: Array[String]): Unit = {
    val config = new SparkConf().setAppName("demo30").setMaster("local[*]")
    val sc = new SparkContext(config)
  }
  def write(sc:SparkContext)={
     // 定義配置
    //val config = new SparkConf().setMaster("local[*]").setAppName("hbase")
    //根據配置實例spark 上下文對象
    //val sc = new SparkContext(config)
    //定義hbase 連接器
    val conf = HBaseConfiguration.create()
    // 添加連接配置
    conf.set("hbase.zookeeper.quorum", "node01:2181,node02:2181,node03:2181")
    //讀取數據
    val data = sc.textFile("./data/hbase.txt").map(x => {
      // 按照\t 切分 返回元組
      val split = x.split("\t");
      (split(0), split(1), split(2), split(3))
    })
    // 將每一條數據進行轉換爲 put
    val value = data.map(x => {
      // 定義put 添加信息
      val put = new Put(Bytes.toBytes(UUID.randomUUID().toString.substring(0, 10).toUpperCase))
      //向put 添加value
      put.addImmutable(Bytes.toBytes("message"), Bytes.toBytes("name"), Bytes.toBytes(x._1))
      put.addImmutable(Bytes.toBytes("message"), Bytes.toBytes("class"), Bytes.toBytes(x._2))
      put.addImmutable(Bytes.toBytes("message"), Bytes.toBytes("sex"), Bytes.toBytes(x._3))
      put.addImmutable(Bytes.toBytes("message"), Bytes.toBytes("province"), Bytes.toBytes(x._4))
      // 返回元組
      (new ImmutableBytesWritable(), put)
    })
    //根據 conf 獲取連接
    val job = new JobConf(conf)
    // 定義輸出類型
    job.setOutputFormat(classOf[TableOutputFormat])
    // 設置要向那個表寫入
    job.set(TableOutputFormat.OUTPUT_TABLE, "student")
    // 提交數據
    value.saveAsHadoopDataset(job)
    println("數據寫入成功")
  }
  def read(sc:SparkContext)={
   // 定義配置
   // val config = new SparkConf().setMaster("local[*]").setAppName("hbase")
    //根據配置實例spark 上下文對象
   // val sc = new SparkContext(config)
    //定義hbase 連接器
    val conf = HBaseConfiguration.create()
    // 添加連接配置
    conf.set("hbase.zookeeper.quorum", "node01:2181,node02:2181,node03:2181")
    // 設置要讀取的那個表中的信息
    conf.set(TableInputFormat.INPUT_TABLE, "student")
    // 添加配置 TableInputFormat 讀取的格式  ImmutableBytesWritable 輸入的key  Result 輸入的value
    var hbase = sc.newAPIHadoopRDD(conf, classOf[TableInputFormat], classOf[ImmutableBytesWritable], classOf[Result])
    //循環遍歷數據
    hbase.foreach {
      case (_, result) =>
        //獲取rowkey
        val rowkey = Bytes.toString(result.getRow)
        // 獲取姓名
        val name = Bytes.toString(result.getValue(Bytes.toBytes("message"), Bytes.toBytes("name")))
        // 獲取班級
        val class_ = Bytes.toString(result.getValue(Bytes.toBytes("message"), Bytes.toBytes("class")))
        // 獲取省份
        val sex = Bytes.toString(result.getValue(Bytes.toBytes("message"), Bytes.toBytes("sex")))
        // 獲取省份
        val province = Bytes.toString(result.getValue(Bytes.toBytes("message"), Bytes.toBytes("province")))
        // 輸出
        println(s"rowKey ${rowkey} name ${name}  class ${class_} sex ${sex} province ${province}")
    }

    sc.stop()
  }
}

 

 

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