spark-1.6.x的学习总结

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

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

特点:

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

回顾MapReduce 的 Shuffle过程 见图
这里写图片描述

这里写图片描述

hadoop慢的原因:
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论文内容介绍:
这里写图片描述

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!!!!

http://www.cnblogs.com/fxjwind/p/3514203.html

容错机制:
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论文原图如下:
这里写图片描述

这里写图片描述

这里写图片描述

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

如图:
这里写图片描述

任务的调度详解:

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的故障探测器有关)

参考:

http://www.tuicool.com/articles/VrqqYne

http://www.cnblogs.com/zhoudayang/p/5007759.html

如果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 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 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
其次是把单列值变成了Tuple元组JavaPairRDD

分组取TopN

GroupTopN.java

score.txt
把数据文件读入成为JavaRDD
因为我们后面要去做分组,所以呢我们先变成键值对JavaPairRDD

二次排序

SecondSortKey.java
sort.txt
首先定义需要排序的列,为需要排序的列提供getter和setter和hashcode和equals方法
同时需要一个构造器,因为外部会去new这个东西
重新 greater, g r e a t e r , greater eq, e q , less, less l e s s eq, compare, compareTo
当定义好二次排序所需要的键之后呢,我们就是需要通过transformation转换new出来我们的自定义key,
然后通过JavaPairRDD

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 和Java Bean传进来就可以了,sqlContext.createDataFrame(students, T.class);
RDD2DataFrameDynamic.java
通过动态构建StructType来动态生成这个DataFrame,StructType里面需要的StructField
可以通过RowFactory.create来创建Row,把JavaRDD 和 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 –> DataFrame
最终存储df.write().format(“json”).save(“goodStudentJson”);

JDBCDataSource.java
这里JDBC重要的是Map

开窗函数

这里我们通过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,构建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”)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值