官方定义:spark是一个基于内存的分布式计算框架

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

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


回顾MapReduce 的 Shuffle过程 见图

 

[Spark进阶]-- spark-1.6.x-小结_spark

 

[Spark进阶]-- spark-1.6.x-小结_Boo_02

hadoop慢的原因:
DISK IO 输入输出DISK IO,Shuffle阶段也是DISK IO


spark快的原因:
1、基于内存
2、DAG的优化


运行模式:
1、Local

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


2、Yarn (ResourceManager NodeManager) HA(zookeeper)
(1)Yarn-client
Driver和Client在一台物理节点上面运行

 

Driver会被资源调度的ResourceManager选一台空闲的物理节点去运行

(2)Yarn-cluster 如果代码里写有setMaster("local"),会报错!应该是setMaster("yarn-cluster")

 

 

 

 

3、 Mesos

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​

 

 

 

 

 

action和transform操作:

伯克利大学spark论文内容介绍:

[Spark进阶]-- spark-1.6.x-小结_数据_03

1、transformation 延迟操作:从RDD 到 RDD

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

 

 

 

缓存策略12种:​​http://tech.meituan.com/spark-tuning-basic.html​

 

 

 

源码如下:

 

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.spark.storage

import java.io.{Externalizable, IOException, ObjectInput, ObjectOutput}
import java.util.concurrent.ConcurrentHashMap

import org.apache.spark.annotation.DeveloperApi
import org.apache.spark.util.Utils

/**
* :: DeveloperApi ::
* Flags for controlling the storage of an RDD. Each StorageLevel records whether to use memory,
* or ExternalBlockStore, whether to drop the RDD to disk if it falls out of memory or
* ExternalBlockStore, whether to keep the data in memory in a serialized format, and whether
* to replicate the RDD partitions on multiple nodes.
*
* The [[org.apache.spark.storage.StorageLevel$]] singleton object contains some static constants
* for commonly useful storage levels. To create your own storage level object, use the
* factory method of the singleton object (`StorageLevel(...)`).
*/
@DeveloperApi
class StorageLevel private(
private var _useDisk: Boolean, //使用磁盘
private var _useMemory: Boolean, //使用内存
private var _useOffHeap: Boolean, //使用堆内存
private var _deserialized: Boolean, //反序列化
private var _replication: Int = 1) //副本数量,默认是1
extends Externalizable {

// TODO: Also add fields for caching priority, dataset ID, and flushing.
private def this(flags: Int, replication: Int) {
this((flags & 8) != 0, (flags & 4) != 0, (flags & 2) != 0, (flags & 1) != 0, replication)
}

def this() = this(false, true, false, false) // For deserialization

def useDisk: Boolean = _useDisk
def useMemory: Boolean = _useMemory
def useOffHeap: Boolean = _useOffHeap
def deserialized: Boolean = _deserialized
def replication: Int = _replication

assert(replication < 40, "Replication restricted to be less than 40 for calculating hash codes")

if (useOffHeap) {
require(!useDisk, "Off-heap storage level does not support using disk")
require(!useMemory, "Off-heap storage level does not support using heap memory")
require(!deserialized, "Off-heap storage level does not support deserialized storage")
require(replication == 1, "Off-heap storage level does not support multiple replication")
}

override def clone(): StorageLevel = {
new StorageLevel(useDisk, useMemory, useOffHeap, deserialized, replication)
}

override def equals(other: Any): Boolean = other match {
case s: StorageLevel =>
s.useDisk == useDisk &&
s.useMemory == useMemory &&
s.useOffHeap == useOffHeap &&
s.deserialized == deserialized &&
s.replication == replication
case _ =>
false
}

def isValid: Boolean = (useMemory || useDisk || useOffHeap) && (replication > 0)

def toInt: Int = {
var ret = 0
if (_useDisk) {
ret |= 8
}
if (_useMemory) {
ret |= 4
}
if (_useOffHeap) {
ret |= 2
}
if (_deserialized) {
ret |= 1
}
ret
}

override def writeExternal(out: ObjectOutput): Unit = Utils.tryOrIOException {
out.writeByte(toInt)
out.writeByte(_replication)
}

override def readExternal(in: ObjectInput): Unit = Utils.tryOrIOException {
val flags = in.readByte()
_useDisk = (flags & 8) != 0
_useMemory = (flags & 4) != 0
_useOffHeap = (flags & 2) != 0
_deserialized = (flags & 1) != 0
_replication = in.readByte()
}

@throws(classOf[IOException])
private def readResolve(): Object = StorageLevel.getCachedStorageLevel(this)

override def toString: String = {
s"StorageLevel($useDisk, $useMemory, $useOffHeap, $deserialized, $replication)"
}

override def hashCode(): Int = toInt * 41 + replication

def description: String = {
var result = ""
result += (if (useDisk) "Disk " else "")
result += (if (useMemory) "Memory " else "")
result += (if (useOffHeap) "ExternalBlockStore " else "")
result += (if (deserialized) "Deserialized " else "Serialized ")
result += s"${replication}x Replicated"
result
}
}


/**
* Various [[org.apache.spark.storage.StorageLevel]] defined and utility functions for creating
* new storage levels.
*/
object StorageLevel {

val NONE = new StorageLevel(false, false, false, false)
/**使用未序列化的Java对象格式,将数据全部写入磁盘文件中*/
val DISK_ONLY = new StorageLevel(true, false, false, false)
val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)
/**使用未序列化的Java对象格式,将数据保存在内存中。如果内存不够存放所有的数据,则数据可能就不会进行持久化。
那么下次对这个RDD执行算子操作时,那些没有被持久化的数据,需要从源头处重新计算一遍。
这是默认的持久化策略,使用cache()方法时,实际就是使用的这种持久化策略**/
val MEMORY_ONLY = new StorageLevel(false, true, false, true)
val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)
/**基本含义同MEMORY_ONLY。唯一的区别是,会将RDD中的数据进行序列化,RDD的每个partition会被序列化成一个字节数组。
这种方式更加节省内存,从而可以避免持久化的数据占用过多内存导致频繁GC**/
val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)
val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)
/**使用未序列化的Java对象格式,优先尝试将数据保存在内存中。如果内存不够存放所有的数据,会将数据写入磁盘文件中,
下次对这个RDD执行算子时,持久化在磁盘文件中的数据会被读取出来使用。**/
val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)
val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)
/**基本含义同MEMORY_AND_DISK。唯一的区别是,会将RDD中的数据进行序列化,RDD的每个partition会被序列化成一个字节数组。
这种方式更加节省内存,从而可以避免持久化的数据占用过多内存导致频繁GC。**/
val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)
val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)
/**关闭堆内存:使用Tachyon分布式内存*/
val OFF_HEAP = new StorageLevel(false, false, true, false)

/**
* :: DeveloperApi ::
* Return the StorageLevel object with the specified name.
*/
@DeveloperApi
def fromString(s: String): StorageLevel = s match {
case "NONE" => NONE
case "DISK_ONLY" => DISK_ONLY
case "DISK_ONLY_2" => DISK_ONLY_2
case "MEMORY_ONLY" => MEMORY_ONLY
case "MEMORY_ONLY_2" => MEMORY_ONLY_2
case "MEMORY_ONLY_SER" => MEMORY_ONLY_SER
case "MEMORY_ONLY_SER_2" => MEMORY_ONLY_SER_2
case "MEMORY_AND_DISK" => MEMORY_AND_DISK
case "MEMORY_AND_DISK_2" => MEMORY_AND_DISK_2
case "MEMORY_AND_DISK_SER" => MEMORY_AND_DISK_SER
case "MEMORY_AND_DISK_SER_2" => MEMORY_AND_DISK_SER_2
case "OFF_HEAP" => OFF_HEAP
case _ => throw new IllegalArgumentException(s"Invalid StorageLevel: $s")
}

/**
* :: DeveloperApi ::
* Create a new StorageLevel object without setting useOffHeap.
*/
@DeveloperApi
def apply(
useDisk: Boolean,
useMemory: Boolean,
useOffHeap: Boolean,
deserialized: Boolean,
replication: Int): StorageLevel = {
getCachedStorageLevel(
new StorageLevel(useDisk, useMemory, useOffHeap, deserialized, replication))
}

/**
* :: DeveloperApi ::
* Create a new StorageLevel object.
*/
@DeveloperApi
def apply(
useDisk: Boolean,
useMemory: Boolean,
deserialized: Boolean,
replication: Int = 1): StorageLevel = {
getCachedStorageLevel(new StorageLevel(useDisk, useMemory, false, deserialized, replication))
}

/**
* :: DeveloperApi ::
* Create a new StorageLevel object from its integer representation.
*/
@DeveloperApi
def apply(flags: Int, replication: Int): StorageLevel = {
getCachedStorageLevel(new StorageLevel(flags, replication))
}

/**
* :: DeveloperApi ::
* Read StorageLevel object from ObjectInput stream.
*/
@DeveloperApi
def apply(in: ObjectInput): StorageLevel = {
val obj = new StorageLevel()
obj.readExternal(in)
getCachedStorageLevel(obj)
}

private[spark] val storageLevelCache = new ConcurrentHashMap[StorageLevel, StorageLevel]()

private[spark] def getCachedStorageLevel(level: StorageLevel): StorageLevel = {
storageLevelCache.putIfAbsent(level, level)
storageLevelCache.get(level)
}
}

 

 

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

 

 

 

但是一旦checkpoint则会封装Job!!!!

 

容错机制:
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的数量来决定)

 

伯克利大学spark论文原图如下:

[Spark进阶]-- spark-1.6.x-小结_数据_04

                      

[Spark进阶]-- spark-1.6.x-小结_Boo_05

 

[Spark进阶]-- spark-1.6.x-小结_数据_06

 

 

 

Cluster
Worker Node
Executor 进程 <--> Port <--> JVM 里面有HEAP堆内存

 

 

Thread Pool <--> 每个Thread 跑一个或多个Task

 

如图:

[Spark进阶]-- spark-1.6.x-小结_spark_07

 

任务的调度详解:
1、最开始的时候得把集群启动好,换句话说就是worker node哪些节点注册到Master上面来
我们开发好程序打个JAR上去运行,spark-submit提交任务!
2、开始运行,Driver先会运行,启动DAGScheduler 启动TaskScheduler

 

当我们taskScheduler.start()运行的时候,它会去找前面的Master资源的主节点要资源

 

3、Master就会去集群上面找空闲的Worker让Worker启动起来需要的Executor,申请的所谓的资源就是Executors,接着Executors会反向注册到TaskScheduler,这是TaskScheduler手头上就有了可用的资源列表

4、接下来DAGScheduler开始画DAG图,切分Stage,封装Task,封装TaskSet,发给TaskScheduler

5、TaskScheduler把里面的Task拿出来找个手头的executor去运行,如果Task发到了Executor上面去,会到里面被封装为TaskRunner,会从线程池里面拿一个Thread去执行

6、如果执行完毕,Results结果会被返回到TaskScheduler上面来,所以我们的Driver在哪里,我们的结果就在哪里!

 

 

那我们说如果这个Task任务失败了,TaskScheduler重新发送,重新去计算,如果出现卡了,我们叫struggling task,如果要解决这类问题,我们可以事先开启竞争模式,所谓的竞争模式就是找个备胎,speculation设置为True就OK(在spark配置文件中设置"spark.speculation=true"。这主要和Akka的故障探测器有关)

 

 


 

 

如果Task反复重试都失败,就认为TaskSet失败也就是对应着一个Stage失败,就会反馈给DAGScheduler,那接下来就还是重试Stage,如果stage多次重试
失败,那么就认为JOB失败了,那重试Job,如果Job反复失败了,就认为Application运行出错了


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

Spark Core---------------->代码举例
本质上就是在操作一个个RDD
Join操作和Cogroup操作

 

JoinAndCogroup.java

 

 

package com.day.java;

import java.util.ArrayList;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.VoidFunction;

import scala.Tuple2;

/**
*
* @author root
* java测试join和cogroup区别
* join:相当于排列组合
* cogroup:合并相同key的value
*/
public class JoinAndCoGroupjav {

public static void main(String []args){
//创建Conf
SparkConf conf=new SparkConf().setAppName("JoingAndCoGroup").setMaster("local");
JavaSparkContext sc=new JavaSparkContext(conf);
//创建2个元组List集合
List> stu=new ArrayList>(); //姓名和id
List> core=new ArrayList>();//id和分数

//存放学生数据
stu.add(new Tuple2(1,"tom"));
stu.add(new Tuple2(2,"jim"));
stu.add(new Tuple2(3,"cassie"));
//存放分数数据
core.add(new Tuple2(1,80));
core.add(new Tuple2(1,30));
core.add(new Tuple2(2,90));
core.add(new Tuple2(2,92));
core.add(new Tuple2(2,90));
core.add(new Tuple2(3,80));
core.add(new Tuple2(3,86));
core.add(new Tuple2(3,87));
//进行paris
JavaPairRDD stuRDD=sc.parallelizePairs(stu);
JavaPairRDD coreRDD=sc.parallelizePairs(core);

//测试使用JavaPairRDD接收并按join方式合并
// JavaPairRDD> stuTupl=stuRDD.join(coreRDD);
//遍历合并结果
/*stuTupl.foreach(new VoidFunction>>() {
private static final long serialVersionUID = 1L;

@Override
public void call(Tuple2> t)
throws Exception {
System.out.println("id is "+t._1);
System.out.println("name is "+t._2._1);
System.out.println("score is "+t._2._2);
}

});*/
//测试使用JavaPairRDD接收并按cogroup方式合并
JavaPairRDD,Iterable>> coreTupl=stuRDD.cogroup(coreRDD);
//遍历并且获取结果
coreTupl.foreach(new VoidFunction,Iterable>>>(){

@Override
public void call(
Tuple2, Iterable>> t)
throws Exception {
System.out.println("============");
System.out.println("id is "+t._1());
System.out.println("name is "+t._2._1);
System.out.println("score is "+t._2._2);
}
});
sc.close();
}
}

 

 

Join 
(1,"cassie") (2,"jack")
(1,100) (2,90) (1,101) (2,91) 

 

 

(1,"cassie",100) (1,"cassie",101) (2,"jack",90) (2,"jack",91)

 

 

Cogroup
(1,"cassie") (2,"jack")
(1,100) (2,90) (1,101) (2,91) 
(1,("cassie"),(100,101)) (2,("jack"),(90,91))


广播变量

 

本质上是把这个变量给广播到所有的Worker上,这就避免了同一份数据被多次拷贝到Tasks里面去,

 

如果是final int f=3也就罢了,但如果是一个词表,那最好就广播出去

BroadCastValue.java

 

package com.day.java;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.broadcast.Broadcast;

/**
*
* @author root
* java测试第一类广播变量:不可改变值,可读的广播变量
*/
public class BroadLCastValueJava {

public static void main(String []args){
SparkConf conf=new SparkConf().setAppName("BroadCast").setMaster("local[1]");
//创建conf
JavaSparkContext sc=new JavaSparkContext(conf);
//创建第一类广播变量
final Broadcast broadcast=sc.broadcast(1);
//创建list
List list=Arrays.asList(1,2,3,6);
//转换为JavaRDD
JavaRDD broadRDD=sc.parallelize(list);
//遍历
JavaRDD results = broadRDD.map(new Function() {
@Override
public Integer call(Integer v1) throws Exception {
return v1*2;
}

});
results.foreach(new VoidFunction(){
@Override
public void call(Integer t) throws Exception {
System.out.println("结果:"+t);
}});
sc.close();
}
}

 

 

final Broadcast<Integer> broadCastFactor = sc.broadcast(f);
broadCastFactor.value()
值得注意的一点就是,这个广播变量是只读的,不能更改


累加器
Accumulator

 

 

AccumulatorValue.java

 

 

package com.day.java;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.Accumulator;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.VoidFunction;
/**
*
* @author root
*广播变量的第二种模式:可以修改值,即累加器
*/
public class AccumulatorValue {

public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("AccumulatorValue").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);

//定义广播变量
final Accumulator sum = sc.accumulator(0);
List list = Arrays.asList(1,2,3,4,5);
JavaRDD listRDD = sc.parallelize(list);

listRDD.foreach(new VoidFunction() {

private static final long serialVersionUID = 1L;

@Override
public void call(Integer value) throws Exception {
//累加变量
sum.add(value);
System.out.println(sum.value());
}
});

System.out.println(sum.value());

sc.close();
}
}

 

 

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


TopN的操作

 

 

TopN.java

 

 

package com.day.java;

import java.util.List;

import org.apache.spark.HashPartitioner;
import org.apache.spark.Partitioner;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.PairFunction;

import scala.Tuple2;
/**
*
* @author root
* 获取文件top.txt中的前三个最高分数
*
* 9
7
6
*
*/
public class TopN {

public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("TopN").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);

JavaRDD lines = sc.textFile("top.txt");

//也可以直接重新分区,合并分区
lines.repartition(3);
//将单列变为2列的元组()
JavaPairRDD pairs = lines.mapToPair(new PairFunction() {

private static final long serialVersionUID = 1L;

@Override
public Tuple2 call(String line) throws Exception {
return new Tuple2(Integer.valueOf(line),line);
}
});

//如果使用rdd的第4大特性,需要RDD是k-v格式
// pairs.partitionBy(new HashPartitioner(3));

JavaPairRDD sorted = pairs.sortByKey(false);
//获取结果值
JavaRDD results = sorted.map(new Function, String>() {

private static final long serialVersionUID = 1L;

@Override
public String call(Tuple2 tuple) throws Exception {
return tuple._2;
}
});
//通过take方法获取前3个数
List list = results.take(3);
for(String s : list){
System.out.println(s);
}

sc.close();
}
}

 

 

 

top.txt

 

3
5
6
7
1
4
5
6
9
0
3

 

 

首先将文件读入为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.6.1-bin-hadoop2.6/student_infos.txt' "
+ "INTO TABLE student_infos");
hiveContext.sql("LOAD DATA LOCAL INPATH '/usr/hadoopsoft/spark-1.6.1-bin-hadoop2.6/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")