spark 学习总结

spark是一个基于内存的分布式计算框架

它会使得计算速度以及开发速度快!

One stack rule them all !!!
一站解决所有问题
热查询(Hive)
批处理(MapReduce)
实时流计算(Storm)

回顾MapReduce 的 Shuffle过程 见图

hadoop慢
DISK IO 输入输出DISK IO,Shuffle阶段也是DISK IO

spark快
基于内存
DAG的优化

运行模式
Local

Standalone (Master Worker) HA(zookeeper)
client模式
Driver和Client在一台物理节点上面运行
cluster模式
Driver会被资源调度的Master选一台空闲的物理节点去运行

Yarn (ResourceManager NodeManager) HA(zookeeper)
Yarn-client
Driver和Client在一台物理节点上面运行
Driver会被资源调度的ResourceManager选一台空闲的物理节点去运行
Yarn-cluster 如果代码里写有setMaster("local"),会报错

RDD
Resilient Distributed Dataset
弹性分布式数据集
就是数据一开始加载过来到内存,从RDD到RDD的转换其实是个抽象的概念,或者换句话说就是瞬时转变的状态

1,a list of partitions (partition是个物理概念,就是在一台物理节点里面的一片数据)
2,A computing function of each Split (我们说Split是读入数据的时候的概念,正常情况下从HDFS来读取,Split就相当于上面的partition,也就是
Block)
3,a list of dependencies 一组依赖
4,可选项,如果RDD里面的元素是元组key value格式,我们可以自己传入Partitioner
pairs.partitionBy(new HashPartitioner(3));
5,可选项,默认情况数据分片是有prefered locations(优先计算的位置),但是我们也可以指定

lines.repartition(3);
我们说举例,譬如说filter过后,一个partition剩下的数据量比较少了,那可以应用这个repartition让它合并

spark的操作算子
http://homepage.cs.latrobe.edu.au/zhe/ZhenHeSparkRDDAPIExamples.html

transformation 延迟操作
从RDD 到 RDD

Action立即执行
从RDD 到 结果或者存储
碰到Action就会封装一个Job sc.runJob(rdd)

缓存策略12种
StorageLevel.NONE
StorageLevel.MEMORY_ONLY (默认的缓存策略)
...

cache() = persist() = persist(StorageLevel.MEMORY_ONLY)
StorageLevel.MEMORY_ONLY 它是只存在内存,而且如果内存不够,直接不存了,那读取呢?一部分从内存读,一部分从原始文件读
StorageLevel.MEMORY_AND_DISK 首先尽量往内存存储,如果内存不够,存在数据所在的本地磁盘!
_ser 做下序列化
_2 有个副本

如何选择RDD的持久化策略
1. cache() MEMORY_ONLY
2. MEMORY_ONLY_SER
3. _2
4. 能内存不使用磁盘

如果RDD是成本很高很耗时才算出来的,我们可以StorageLevel.MEMORY_AND_DISK,当然更多选择的doCheckpoint()

容错
1, 默认的容错重新计算,那这个地方,从新计算RDD,它并不是说所有的Partition都参与,哪个计算错,哪个就重新计算
2, Lineage(是相对RDD来说的)如果很长,可以用persist() 内存
3, doCheckpoint 磁盘 SparkContext.setCheckPointDir("hdfs://...")

宽窄依赖为什么要有?
切分stage
什么是宽依赖?
宽依赖就是父RDD里面的partition会去向子RDD里面的多个partitions,多机到多机的数据传输(shuffle),我们就称之为宽依赖,除外都是窄依赖
宽窄依赖spark内核引擎主要是根据咱们的代码里面的算子来定的
map
filter
union
join
groupBy

Application <--> Driver program (main) <--> new SparkContext <--> new DAGScheduler , new TaskSheduler ( SparkDeployBackend)
Job (根据Action来划分)
Stage (根据宽窄依赖来划分)
Task (根据stage内部的partition的数量来决定)

Cluster
Worker Node
Executor 进程 <--> Port <--> JVM 里面有HEAP堆内存
Thread Pool <--> 每个Thread 跑一个或多个Task

什么是DAG的优化?
就是首先根据宽窄依赖切分出了stages,如果是窄依赖就不再划分stage,这个是优化的前提
那根据一个partition会对应一个Task,做出来优化,这种在一个stage内部一个partition对应一个Task我们叫做pipeline
而这种pipeline的效果就是1+1+...+1 = N
反过来说如果没有这种优化,就好比 1+1=2; 2+1=3; 每次都是封装一个Task,Task本身的信息是不是也占网络传输啊?
其次每次Task的输出都要记录位置吧,下次的时候还得再来读

任务的调度
最开始的时候得把集群启动好吧,换句话说就是worker node哪些节点注册到Master上面来
我们开发好程序打个JAR上去运行,spark-submit
开始运行Driver先会运行,启动DAGScheduler 启动TaskScheduler
当我们taskScheduler.start()运行的时候,它会去找前面的Master资源的主节点要资源,Master就会去集群上面找空闲的Worker让Worker启动起来需要
的Executor,申请的所谓的资源无非就是Executors,接着Executors会反向注册到TaskScheduler,这是TaskScheduler手头上就有了可用的资源列表
接下来DAGScheduler开始画DAG图,切分Stage吧,封装Task,封装TaskSet,发给TaskScheduler,TaskScheduler把里面的Task拿出来找个手头的executor
去运行,如果Task发到了Executor上面去,会到里面被封装为TaskRunner,会从线程池里面拿一个Thread去执行,如果执行完毕,是不是Results结果会被
返回到TaskScheduler上面来,所以我们的Driver在哪里,我们的结果就在哪里!

那我们说如果这个Task任务失败了,TaskScheduler重新发送,重新去计算,如果出现卡了,我们叫struggling task,如果要解决这类问题,我们可以事先
开启竞争模式,所谓的竞争模式就是找个备胎吧,speculation设置为True就Okay了
如果Task反复重试都失败,就认为TaskSet失败也就是对应着一个Stage失败,就会反馈给DAGScheduler,那接下来就还是重试Stage,如果stage多次重试
失败,那么就认为JOB失败了,那重试Job,如果Job反复失败了,就认为Application运行出错了吧


Spark Core
本质上就是在操作一个个RDD
Join操作和Cogroup操作
JoinAndCogroup.java
Join
(1,"yasaka") (2,"jack")
(1,100) (2,90) (1,101) (2,91)
(1,"yasaka",100) (1,"yasaka",101) (2,"jack",90) (2,"jack",91)
Cogroup
(1,"yasaka") (2,"jack")
(1,100) (2,90) (1,101) (2,91)
(1,("yasaka"),(100,101)) (2,("jack"),(90,91))

广播变量
本质上是把这个变量给广播到所有的Worker上,这就避免了同一份数据被多次拷贝到Tasks里面去,如果是final int f=3也就罢了,如果是一个词表,
那最好就广播出去
BroadCastValue.java
final Broadcast<Integer> broadCastFactor = sc.broadcast(f);
broadCastFactor.value()
值得注意的一点就是,这个广播变量是只读的,不能更改

累加器
Accumulator
AccumulatorValue.java
final Accumulator<Integer> sum = sc.accumulator(0);
使用的时候
sum.add(value);
值得注意的一点是,在task里面对代码来说就是在重写的Call方法里面是获取不到的吧
只能最后在Driver程序中查看
sum.value()

TopN的操作
TopN.java
top.txt
首先将文件读入为JavaRDD<String>
其次是把单列值变成了Tuple元组JavaPairRDD<Integer, String>
接着排序sortByKey(false)
取topN无非就是用一下take()方法就可以了

分组取TopN
GroupTopN.java
score.txt
把数据文件读入成为JavaRDD<String>
因为我们后面要去做分组,所以呢我们先变成键值对JavaPairRDD<String, Integer>
利用groupByKey()把相同key的给凑一堆儿去JavaPairRDD<String, Iterable<Integer>>
在接下来的mapToPair方法里面我们并没有改变它的值,也没有改变它的格式以及类型,我们只是在里面用冒泡算法给做了小排序取里面的top3
最后foreach把它打印出来

二次排序!!!
SecondSortKey.java
sort.txt
首先定义需要排序的列,为需要排序的列提供getter和setter和hashcode和equals方法
同时需要一个构造器,因为外部会去new这个东西
重新$greater, $greater$eq, $less, $less$eq, compare, compareTo
当定义好二次排序所需要的键之后呢,我们就是需要通过transformation转换new出来我们的自定义key,
然后通过JavaPairRDD<SecondSortKey, String> sortedPairs = pairs.sortByKey(false);
排完序之后,key不是我们关系,我们需要把值再抽取出来
【自定义的key需要去实现一些个接口(extends Ordered[SecondSortKey] with Serializable)】


Spark SQL

可以兼容Hive,读取Hive里面的表数据,可以从Hive/JDBC/JSON数据源读取数据过来直接变成DataFrame
里面的最佳拍档是DataFrame,数据框,"表"

传入一条SQL语句过去-->首先通过sql parser分析SQL语句-->接着会有Analyzer来生成逻辑执行计划,Logical Plan-->会通过optimizer来优化这个逻辑执行
计划,spark SQL牛的地方就是这个optimizer做的很不错,join where --> spark planner这个组件去生成物理执行计划
另外呢还有个钨丝计划,它会在底层使用内存的上面进行优化

我们如何理解DataFrame的存储呢?如果是一个RDD,里面存的是一个个的Person对象,相反DataFrame是列存储,相同列的数据会被存储在一起,当然还是
指的内存中,所以DataFrame的性能呢要比这个RDD好很多倍!DataSet会未来Spark SQL小组去重点研究的集合,会有这种数据集的API出现,性能会比DF更好

创建DataFrame
DataFrameCreate.java
students.json
我们通过sqlContext.read().json()读进来
show();方法可以打印出来

DataFrame DSL 操作
DataFrameOperation.java
students.json
打印元数据
根据列查询
还可以查询同时并计算
过滤
还可以根据某列分组再count计数
这个地方讲过之后可以把之前wc改写通过DataFrame来实现


读取HDFS过来,生成的是RDD,之后构建DF

DataFrame两种方式构建
在使用Spark SQL之前我们肯定需要SQLContext(sc)
RDD2DataFrameReflection.java
students.txt
通过反射,java的版本需要java bean,scala的版本需要一个case class
把JavaRDD<T> 和Java Bean传进来就可以了,sqlContext.createDataFrame(students, T.class);
RDD2DataFrameDynamic.java
通过动态构建StructType来动态生成这个DataFrame,StructType里面需要的StructField
可以通过RowFactory.create来创建Row,把JavaRDD<Row> 和 structType传进来对不对,sqlContext.createDataFrame(rows, structType);
我们可以把获取到DataFrame注册为一张临时表registerTempTable
最后就可以通过sqlContext.sql()


读取默认数据源
JSONDataSource.java
students.json
读入sqlContext.read().json文件进来,因为是默认格式,所以直接就是DataFrame,然后就可以注册临时表registerTempTable
然后就可以进行过去,这里的案例是把学生分数大于80的选出来,接着把DataFrame转化为了RDD
下面呢就是读入了另外一个json文件然后注册临时表,这里最重要的就是拼写了一个SQL语句,在SQL语句里面就用到了in关键字,去过滤好学生的数据
sqlContext.sql执行后就把好学生的info信息就查询出来
一个studentScoreRDD和studentInfoRDD直接的join操作,得到好学生的分数以及信息,这个时候要想存回为一个JOSN文件,我们还要转回为DataFrame
JavaRDD<> --> JavaRDD<Row> --> DataFrame
最终存储df.write().format("json").save("goodStudentJson");

JDBCDataSource.java
这里JDBC重要的是Map<String,String> options = new HashMap<String,String>();里面需要传数据库的url,还要表名dbtable,
sqlContext.read().format("jdbc").options(options).load();
读进来时两个DF,DF转为RDD,一个studentScoreRDD和studentInfoRDD直接的join操作,还是一个RDD,转为JavaRDD<Row>,用RDD里面的filter进行过滤,
然后可以foreach可以把每个元素进行存储,当然这是需要DriverManager.getConnection("jdbc:mysql://spark001:3306/test", "root", "123123");
./bin/spark-submit --master local --class com.spark.study.sql.JDBCDataSource --driver-class-path ./lib/mysql-connector-java-5.1.32-bin.jar sparksql.jar

HiveDataSource.java
需要HiveContext
需要首先在HIVE里面准备两张表的数据
hiveContext.sql("LOAD DATA LOCAL INPATH '/usr/hadoopsoft/spark-1.3.1-bin-hadoop2.4/student_infos.txt' "
+ "INTO TABLE student_infos");
hiveContext.sql("LOAD DATA LOCAL INPATH '/usr/hadoopsoft/spark-1.3.1-bin-hadoop2.4/student_scores.txt' "
+ "INTO TABLE student_scores");
hiveContext.sql传入SQL语句关联两张表,查询成绩大于80分的学生,返回的是一个DataFrame
接着就是saveAsTable存回HIVE里面去,Bingo!!

SaveMode存储的时候可以选择不同的策略


开窗函数!!!
这里我们通过HiveContext去取数据
hiveContext.sql("LOAD DATA "
+ "LOCAL INPATH '/usr/hadoopsoft/spark-1.5.0-bin-hadoop2.4/sales.txt' "
+ "INTO TABLE sales");
iphone7 cellphone 1000000
...
在hiveContext.sql里面首先有个子查询,row_number() OVER (PARTITION BY category ORDER BY revenue DESC) rank
row_number()开窗函数的作用是把分组后,里面每一组的数据从开头到结尾编号,从1开始到N,如果像案例里面的DESC,那就是最大的那个值为编号1
编号在哪里?就在rank里!
在外面的查询就可以根据子查询的数据来进行过滤,根据刚刚的rank一列进行过滤,顺便product,category,revenue也都有了
(分组取TopN可以在这里用开窗函数实现)


Spark SQL内建的函数

DailySale.scala
按日期分组,把总销售额给统计出来
加载文件数据,是一个RDD,filter方法过滤脏数据,变为RDD<Row>,构建StructType,sqlContext.createDataFrame构建DataFrame,
// 这里着重说明一下!!!
// 要使用Spark SQL的内置函数,就必须在这里导入SQLContext下的隐式转换
import sqlContext.implicits._
groupBy 分组
agg 聚合
聚合函数里面可以传内建的计算函数比如sum

DailyUV.scala
按日期分组,把user view给统计出来,user view你会了,page view会不会?user view无法就是需要一个distinct()去重步骤
groupBy
agg
countDistinct

上面我们wc可以通过Spark sql来重写,这里我们可以通过Spark SQL里面的内建函数来重写

UDF
文件 --> RDD --> RDD[Row] --> DataFrame --> registerTempTable
sqlContext.udf.register("strLen", (str : String)=> str.length())
使用刚刚注册的UDF函数,无法就是sqlContext.sql("select name, strLen(name) from names")

UDAF
定义了一个类StringCount去继承了UserDefinedAggregateFunction
sqlContext.udf.register("strCount", new StringCount)
sqlContext.sql("select name, strCount(name) from names group by name")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值