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和RDD的知识梳理与总结_第1张图片

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

spark和RDD的知识梳理与总结_第2张图片

参数的说明

--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中的数据可以保存在内存中或者磁盘里面

分布式存储的,可以用于分布式计算

           集合,可以存放很多元素

一个不可变、可分区、里面的元素可并行计算的集合 

spark和RDD的知识梳理与总结_第3张图片

RDD的主要属性

1、数据集的基本组成单位是一组分片(Partition)或一个分区(Partition)列表

每个分片都会被一个计算任务处理,分片数决定并行度。

用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值(默认值2)

 

2、一个函数会被作用在每一个分区。

   Spark中RDD的计算是以分区为单位的,函数会被作用到每个分区上

 

3、一个RDD会依赖于其他多个RDD。

RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。(Spark的容错机制)

spark和RDD的知识梳理与总结_第4张图片

 

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,Iterable))类型的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丢失数据时,可以使用持久化到硬盘的数据恢复。

spark和RDD的知识梳理与总结_第5张图片

 

持久化/缓存(内存)

缓存方法

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

持久化结果

spark和RDD的知识梳理与总结_第6张图片

 

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执行的流程)

spark和RDD的知识梳理与总结_第7张图片

DAG边界

开始:通过SparkContext创建的RDD

结束:触发Action,一旦触发Action就形成了一个完整的DAG

说明:

一个Spark应用中可以有一到多个DAG,取决于触发了多少次Action

 

一个DAG中会有不同的阶段/stage,划分阶段/stage的依据就是宽依赖

一个阶段/stage中可以有多个Task,一个分区对应一个Task

spark和RDD的知识梳理与总结_第8张图片

为什么要划分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

spark和RDD的知识梳理与总结_第9张图片

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()
  }
}

 

 

你可能感兴趣的:(spark,RDD)