Spark知识点讲解

  • Spark
  • Spark环境部署
  • Spark简介
  • 为什么使用Spark
  • Spark的优势
  • Spark技术栈
  • Spark架构设计
  • Spark架构核心组件
  • Spark交互工具
  • Spark API
  • SparkContext
  • SparkSession
  • RDD
  • DataSet
  • DataFrame
  • Spark RDD
  • 概念
  • RDD与DAG
  • RDD的特性
  • RDD编程流程
  • 创建RDD
  • RDD分区
  • RDD的操作
  • RDD转换算子
  • RDD常用算子
  • Spark分布式计算原理
  • RDD的依赖关系
  • DAG的工作原理
  • Spark Shuffle过程
  • RDD优化
  • RDD持久化
  • 检查点
  • 共享变量
  • 分区设计
  • 数据倾斜
  • 装载数据源
  • Spark SQL
  • Spark SQL架构
  • Spark SQL运行原理
  • 优化器
  • SparkSQL API
  • Dataset
  • DataFrame
  • SparkSQL操作外部数据源
  • Parquet文件
  • Hive表
  • RDBMS表
  • SparkSQL函数
  • 内置函数
  • 自定义函数
  • Spark性能优化
  • 序列化
  • SparkGraphX图形数据分析
  • 图的基本概念
  • GraphX核心抽象
  • GraphX API
  • pergel函数


Spark

Spark环境部署

  1. 前置条件:完成Scala环境部署
  2. 配置Scala和Spark

Spark简介

  • 诞生于加州大学伯克利分校AMP实验室,是一个基于内存的分布式计算框架

为什么使用Spark

  • MapReduce编程模型的局限性,只有Map和Reduce两个操作,复杂的逻辑需要大量的样板代码
  • MapReduce处理效率低,Map中间结果写磁盘,Reduce写HDFS,多个Map通过HDFS交换数据,任务调度与启动开销大,不适合迭代处理、交互式处理和流式处理
  • Spark是类Hadoop MapReduce的通用并行框架,Job中间数据结果可以保存在内存,不再需要读写HDFS,比MapReduce平均快10倍以上

Spark的优势

  • 速度快
    基于内存数据处理,比MR快100个数量级以上
    基于硬盘数据处理,比MR快10个数量级以上
  • 易用性
    支持Java、Scala、Python、R语言
    交互式shell方便开发测试
  • 通用性
    一站式解决方案:批处理、交互式查询、实时流处理、图计算及机器学习
  • 随处运行
    YARN、Mesos、EC2、Kubernetes、Standalone、Local

Spark技术栈

spark原理及应用 spark详解_System

  • Spark Core
    核心组件,分布式计算引擎
  • Spark SQL
    高性能的基于Hadoop的SQL解决方案
  • Spark Streaming
    可以实现高吞吐量、具备容错机制的准实时流处理系统
  • Spark GraphX
    分布式图处理框架
  • Spark MLlib
    构建在Spark上的分布式机器学习库

Spark架构设计

spark的运行架构

spark原理及应用 spark详解_spark原理及应用_02

  • 在驱动程序中,通过Spark Context主导应用的执行
  • Spark Context可以连接不同类型的Cluster Manager(Standalone、YARN、Mesos),连接后,获得集群节点上的Executor
  • 一个Worker节点默认一个Executor,可通过SPARK_WORKER_INSTANCES调整
  • 每个应用获取自己的Executor
  • 每个Task处理一个RDD分区

Spark架构核心组件

组件

说明

Application

建立在Spark上的用户程序,包括Driver代码和运行在集群各节点Executor中的代码

Driver Program

驱动程序,Application中的main函数并创建Spark Context

Cluster Manager

在集群(Standalone、Mesos、YARN)上获取资源的外部服务

Worker Node

集群中任何可以运行Application代码的节点

Executor

某个Application运行在workder节点上的一个进程

Task

被送到某个Executor上的工作单元

Job

包含多个Task组成的并行计算,往往由Spark Action触发生成,一个Application中往往产生多个Job

Stage

每个job会被拆分成多组Task,作为一个TaskSet,其名称为Stage

Spark交互工具

  • 本机
    spark-shell --master local[*]
  • Standalone
    spark-shell --master spark://MASTERHOST:7077
  • YARN
    spark-shell --master yarn-client

Spark API

SparkContext

  • 连接Driver与Spark Cluster(Workers)
  • Spark的主入口
  • 每个JVM仅能有一个活跃的Spark Context
  • SparkContext.getOrCreate
import org.apache.spark.SparkConf;
import org.apache.spark.SparkContext;

public class Main {

    public static void main(String... args){
        SparkConf sparkConf = new SparkConf().setMaster("local[*]").setAppName("my_spark");
        SparkContext sparkContext = SparkContext.getOrCreate(sparkConf);
    }
}

SparkSession

  • Spark2.0+应用程序的主入口:包含了Spark Context、SQL Context、Hive Context以及Streaming Context
  • SparkSession.getOrCreate()
import org.apache.spark.sql.SparkSession;

public class Main {

    public static void main(String... args) {
        SparkSession sparkSession = SparkSession
                .builder()
                .master("local[*]")
                .appName("my_spark")
                .getOrCreate();
    }
}

RDD

  • 弹性分布式数据集
  • Spark核心,主要数据抽象

DataSet

  • 从Spark1.6开始引入的新的抽象、特定领域对象中的强类型集合,它可以使用函数或者相关操作并行的进行转换等操作

DataFrame

  • DataFrame是特殊的DataSet

Spark RDD

概念

简单的解释:群的工作节点上的内存中,并执行正确的操作。

复杂的解释:assandra、HBase等、或缓存(内存、内存+磁盘、仅磁盘等),或在故障或缓存收回时重新计算其他RDD分区中的数据

RDD是弹性分布式数据集(Resilient Distributed Datasets)

  • 分布式数据集
    RDD是只读的、分区记录的集合,每个分区分布在集群的不同节点上;
    RDD并不存储真正的数据,只是对数据和操作的描述
  • 弹性
    RDD默认存放在内存中,当内存不足,Spark自动将RDD写入磁盘
  • 容错性
    根据数据血统,可以自动从节点失败总恢复分区

RDD与DAG

  • 两者是Spark提供的核心抽象
  • DAG(有向无环图)反映了RDD之间的依赖关系

RDD的特性

  • 一系列的分区信息,每个任务处理一个分区
  • 每个分区上都有compute函数,计算该分区中的数据
  • RDD之间有一系列的依赖
  • 分区函数决定数据(key-value)分配到哪个分区
  • 最佳位置列表,将计算任务分配到其所处理数据块的存储位置

RDD编程流程

spark原理及应用 spark详解_System_03

创建RDD

第一种:使用集合创建RDD

import org.apache.spark.SparkContext;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.sql.SparkSession;

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

public class Main {

    public static void main(String... args) {
        SparkSession sparkSession = SparkSession
                .builder()
                .master("local[*]")
                .appName("my_spark")
                .getOrCreate();

        SparkContext sc = sparkSession.sparkContext();
        JavaSparkContext jsc = JavaSparkContext.fromSparkContext(sc);
        List<Integer> list = Arrays.asList(1, 2, 3, 4);

        JavaRDD<Integer> data = jsc.parallelize(list);
        System.out.println(data.count());
    }
}

第二种:通过加载文件生成RDD

import org.apache.spark.SparkContext;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.sql.SparkSession;

public class Main {

    public static void main(String... args) {
        SparkSession sparkSession = SparkSession
                .builder()
                .master("local[*]")
                .appName("my_spark")
                .getOrCreate();

        SparkContext sc = sparkSession.sparkContext();
        JavaSparkContext jsc = JavaSparkContext.fromSparkContext(sc);

//        List<Integer> list = Arrays.asList(1, 2, 3, 4);
//        JavaRDD<Integer> data = jsc.parallelize(list);
//        System.out.println(data.count());
        JavaRDD<String> rdd = jsc.textFile("D:\\a.txt");
        System.out.println(rdd.count());
        JavaRDD<String> hdfsRDD = jsc.textFile("hdfs://hadoop000:8020/hello.txt");
    }
}

第三种:其他创建RDD的方法

  • SparkContext.wholeTextFiles();可以针对一个目录中的大量小文件返回<filename,fileContent>作为PairRDD
  • RDD
  • PairRDD
  • SparkContext.sequenceFile,Hadoop SequenceFile的读写支持
  • SparkContext.hadoopRDD()、newAPIHadoopRDD(),从hadoop接口API创建
    -SparkContext.objectFile(),RDD.saveAsObjectFile()的逆操作

RDD分区

分区是RDD被拆分并发送到节点的不同块之一

  • 拥有的分区越多,得到得并行性就越强
  • 每个分区都是被分发到不同worker node得候选者
  • 每个分区对应一个task

RDD的操作

分为lazy和non-lazy两种:

  • Transformation(lazy):也称转换操作、转换算子
  • Actions(non-lazy):立即执行,也称动作操作、动作算子

RDD转换算子

对于转换操作,RDD的所有转换都不会直接计算结果

仅记录作用于RDD上的操作

只有遇到Action算子才会进行真正的计算

spark原理及应用 spark详解_spark_04

RDD常用算子

  • parallelize
JavaRDD<String> javaStringRDD = sc.parallelize(Arrays.asList("abc", "bcd"));
  • makeRDD
    只有scala有makeRDD
sc.makeRDD(List("abc","bcd"));
  • textFile
    调用SparkContext.textFile()方法,从外部存储中读取数据来创建RDD
JavaRDD<String> lines = sc.textFile("F:\\dataexample\\wordcount\\input");

注:textFile支持分区,支持模式匹配,例如把F:\dataexample\wordcount\目录下inp开头的给转换成rdd

JavaRDD<String> lines = sc.textFile("F:\\dataexample\\wordcount\\inp*");

多个路径可以使用逗号分隔,例如:

var lines = sc.textFile("dir1, dir2", 3);
  • filter
    过滤
JavaRDD<String> lines = jsc.textFile("D:\\a.txt");
lines = lines.filter(f -> f.contains("org"));
lines.foreach(f->{
	System.out.println(f);
});
  • map
    map()接收一个函数,把这个函数用于RDD中的每个元素,将函数的返回结果作为结果RDD编程
JavaRDD<Iterable> it = lines.map(f -> Arrays.asList(f.split("\\+")));
System.out.println(it.first());
  • flatMap
    有时候我们希望对某个元素生成多个元素,实现该功能的操作叫做flatMap(),flatMap的函数应用于每一个元素,对于每一个元素返回的多个元素组成的迭代器
JavaRDD<String> result = lines.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterator<String> call(String s) throws Exception {
                return Arrays.asList(s.split("\\+")).iterator();
            }
        });
//lambda
JavaRDD<String> result2 = lines.flatMap(s -> Arrays.asList(s.split("\\+")).iterator());
  • distinct
    用于去重,我们生成的RDD可能有重复的元素,使用distinct方法可以去掉重复的元素,不过此方法设计到混洗,操作开销很大
JavaRDD<String> rdd = jsc.parallelize(Arrays.asList("aa", "bb", "cc", "dd", "bb"));
rdd = rdd.distinct();
rdd.foreach(f -> System.out.println(f));
  • union
    两个RDD进行合并
JavaRDD<String> rdd1 = jsc.parallelize(Arrays.asList("aa", "bb", "cc", "dd", "bb"));
JavaRDD<String> rdd2 = jsc.parallelize(Arrays.asList("ee", "ff"));
JavaRDD<String> unionRDD = rdd1.union(rdd2);
unionRDD.foreach(f -> System.out.println(f));
  • intersection
    返回两个RDD的交集,并且去重,intersection需要混洗数据,比较浪费性能
JavaRDD<String> rdd1 = jsc.parallelize(Arrays.asList("aa", "bb", "cc", "dd", "bb"));
        JavaRDD<String> rdd2 = jsc.parallelize(Arrays.asList("aa", "ee", "ff"));
        JavaRDD<String> intersectionRDD = rdd1.intersection(rdd2);
        intersectionRDD.foreach(f -> System.out.println(f));
  • subtract
    RDD1.subtract(RDD2),返回在RDD1中出现,但是不在RDD2中出现的元素,不去重
JavaRDD<String> subtractRDD = rdd1.subtract(rdd2);
  • cartesian
    RDD1.cartesian(RDD2)返回RDD1和RDD2的笛卡尔积,开销非常大
JavaRDD<String> RDD1 = jsc.parallelize(Arrays.asList("1", "2", "3"));
        JavaRDD<String> RDD2 = jsc.parallelize(Arrays.asList("a", "b", "c"));
        JavaPairRDD<String, String > cartesianRDD = RDD1.cartesian(RDD2);
        List<Tuple2<String, String>> list = cartesianRDD.collect();
        list.forEach(f->{
            System.out.println(f._1 + "<>" + f._2);
        });
  • mapToPair
    输出Map
JavaPairRDD<String, Integer> mapToPairRDD = rdd.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<>(s, 1);
            }
        });
        JavaPairRDD<String, Integer> mapToPairRDD2 = rdd.mapToPair((PairFunction<String, String, Integer>) s -> new Tuple2<>(s, 1));
  • flatMapToPair
    flatMapToPair可以一个元素返回多个,相当于先flatMap,再mapToPair
JavaRDD<String> lines = jsc.textFile("d:\\sample.txt");
        JavaPairRDD<String, Integer> result = lines.flatMapToPair(new PairFlatMapFunction<String, String, Integer>() {
            @Override
            public Iterator<Tuple2<String, Integer>> call(String s) throws Exception {
                List<Tuple2<String, Integer>> tuple2List = new ArrayList<>();
                //至少出现一个空格
                String[] array = s.split("\\s+");
                for (int i = 0; i < array.length; i++) {
                    tuple2List.add(new Tuple2<>(array[i], 1));
                }
                return tuple2List.iterator();
            }
        });

        JavaPairRDD<String, Integer> result2 = lines.flatMapToPair((PairFlatMapFunction<String, String, Integer>) s -> {
            List<Tuple2<String, Integer>> tuple2List = new ArrayList<>();
            //至少出现一个空格
            String[] array = s.split("\\s+");
            for (int i = 0; i < array.length; i++) {
                tuple2List.add(new Tuple2<>(array[i], 1));
            }
            return tuple2List.iterator();
        });
  • combineByKey
    分布式数据集聚合操作的鼻祖
  • reduceByKey
    根据Key对vlue做聚合
JavaRDD<String> lines = jsc.textFile("d:\\sample.txt");
        JavaPairRDD<String, Integer> result = lines.flatMapToPair(new PairFlatMapFunction<String, String, Integer>() {
            @Override
            public Iterator<Tuple2<String, Integer>> call(String s) throws Exception {
                List<Tuple2<String, Integer>> tuple2List = new ArrayList<>();
                //至少出现一个空格
                String[] array = s.split("\\s+");
                for (int i = 0; i < array.length; i++) {
                    tuple2List.add(new Tuple2<>(array[i], 1));
                }
                return tuple2List.iterator();
            }
        });

        JavaPairRDD<String, Integer> result2 = lines.flatMapToPair((PairFlatMapFunction<String, String, Integer>) s -> {
            List<Tuple2<String, Integer>> tuple2List = new ArrayList<>();
            //至少出现一个空格
            String[] array = s.split("\\s+");
            for (int i = 0; i < array.length; i++) {
                tuple2List.add(new Tuple2<>(array[i], 1));
            }
            return tuple2List.iterator();
        });

        JavaPairRDD<String, Integer> result3 = result2.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer i1, Integer i2) throws Exception {
                return i1 + i2;
            }
        });
        Map<String, Integer> map = result3.collectAsMap();
        map.entrySet().forEach(f -> {
            System.out.println(f.getKey() + "<>" + f.getValue());
        });
  • foldByKey
    该函数用于RDD[K,V]根据K将V做折叠、合并处理
  • sortByKey
    sortByKey函数用于对pairRDD按照Key进行排序,第一个参数可以设置true或者false,默认是true
  • groupByKey
    将RDD[key,value]按照key进行分组,形成RDD[key,Interable]的形式,类似sql中的groupby或group_concat
JavaRDD<Tuple2<String, Double>> score = jsc.parallelize(Arrays.asList(new Tuple2("xm",60), new Tuple2("xm", 70), new Tuple2("zs", 90), new Tuple2("zs", 98)));
        JavaPairRDD<String, Double> scoreRDD = JavaPairRDD.fromJavaRDD(score);
        JavaPairRDD<String, Iterable<Double>> scorePairRDD = scoreRDD.groupByKey();
        Map<String, Iterable<Double>> resultMap = scorePairRDD.collectAsMap();
        resultMap.entrySet().forEach(f -> System.out.println("key>>>>" + f.getKey()));
  • cogroup
    groupByKey是对单个RDD的数据进行分组,cogroup是对多个共享同一个键的RDD进行分组;
    例如:
    RDD1.cogroup(RDD2)会将RDD1和RDD2按照相同的key进行分组,得到(key,RDD[key,Iterable[value1],Iterable[value2]])
    cogroup也可以进行多个分组
JavaRDD<Tuple2<String, Double>> scoreDetail1 = jsc.parallelize(Arrays.asList(new Tuple2("zhangsan", 75), new Tuple2("zhangsan", 90), new Tuple2("lisi", 97), new Tuple2("lisi", 96)));
        JavaRDD<Tuple2<String, Double>> scoreDetail2 = jsc.parallelize(Arrays.asList(new Tuple2("zhangsan", 75), new Tuple2("wangwu", 60), new Tuple2("wangwu", 62)));
        JavaRDD<Tuple2<String, Double>> scoreDetail3 = jsc.parallelize(Arrays.asList(new Tuple2("zhangsan", 45), new Tuple2("zhangsan", 24), new Tuple2("lisi", 97), new Tuple2("lisi", 35)));

        JavaPairRDD<String, Double> scoreMap1 = JavaPairRDD.fromJavaRDD(scoreDetail1);
        JavaPairRDD<String, Double> scoreMap2 = JavaPairRDD.fromJavaRDD(scoreDetail2);
        JavaPairRDD<String, Double> scoreMap3 = JavaPairRDD.fromJavaRDD(scoreDetail3);

        JavaPairRDD<String, Tuple3<Iterable<Double>, Iterable<Double>, Iterable<Double>>> scoreReslt = scoreMap1.cogroup(scoreMap2, scoreMap3);
        Map<String, Tuple3<Iterable<Double>, Iterable<Double>, Iterable<Double>>> scoreResultMap = scoreReslt.collectAsMap();
        scoreResultMap.entrySet().forEach(f -> System.out.println("cogroup key:" + f.getKey()));
  • subtractByKey
    删除RDD中键与其他RDD中键相同的元素
  • join
    RDD1.join(RDD2),可以把RDD1,RDD2中的相同的key给连接起来,类似与sql中的join操作
  • fullOuterJoin
    和join,不过fullOouterJoin是全连接
  • rightOuterJoin
    类似右外连接
  • leftOuterJoin
    类似左外连接
  • first【action操作】
    返回第一个元素
  • take【action操作】
    返回第N个元素
  • collect【action操作】
    返回RDD中的所有元素
  • count【action操作】
    返回RDD中的元素的个数
  • countByValue【action操作】
    各元素在RDD中出现的次数
  • reduce【action操作】
    并行整合RDD中的数据
  • aggregate【action操作】
    和Reduce类似
  • fold【action操作】
    一般不使用
  • top【action操作】
    按照顺序返回前N个元素
  • takeOrdered【action操作】
    对RDD元素进行升序排序,取出前n个元素并返回,也可以自定义比较器(这里不介绍),类似于top的相反的方法
  • foreach
    遍历RDD
  • countByKey【action操作】
    按Key进行计数
  • collectAsMap【action操作】
    RDD转换成Map
  • saveAsTextFile【action操作】
    保存到文件
  • saveAsSequenceFile【action操作】
    以SequenceFile的文件格式保存到HDFS
  • saveAsObjectFile【action操作】
  • saveAsHadoopFile【action操作】
  • mapPartitions
    先partition,再把每个partition进行map函数,
    比如,将RDD中的所有数据通过JDBC连接写入数据库,如果使用map函数,可能要为每一个元素都创建一个connection,这样开销很大,如果使用mapPartitions,那么只需要针对每一个分区建立一个connection.
JavaRDD<Integer> rdd = jsc.parallelize(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 1));
        JavaRDD<Integer> mapPartitionRDD = rdd.mapPartitions(new FlatMapFunction<Iterator<Integer>, Integer>() {
            @Override
            public Iterator<Integer> call(Iterator<Integer> it) throws Exception {
                List<Integer> results = new ArrayList<>();
                while (it.hasNext()) {
                    int i = it.next();
                    results.add(i * i);
                }
                return results.iterator();
            }
        });
        mapPartitionRDD.foreach(f -> System.out.println(f));
  • mapPartitionsWithIndex
    与mapPartitionWithIndex类似,也是按照分区进行的map操作,不过mapPartitionsWithIndex传入的参数多了一个分区的值,下面举个例子,为统计各个分区中的元素 (稍加修改可以做统计各个分区的数量)
  • HashPartitioner
    默认的分区就是HashPartition分区,默认分区不再介绍,下面介绍HashPartition的使用
  • RangePartitioner
    我理解成范围分区器
    使用一个范围,将范围内的键分配给相应的分区。这种方法适用于键中有自然排序,键不为负。本文主要介绍如何使用,原理以后再仔细研究,以下代码片段显示了RangePartitioner的用法

Spark分布式计算原理

spark原理及应用 spark详解_spark_05

RDD的依赖关系

  • Lineage:血统、遗传
    RDD最重要的特征之一,保存了RDD的依赖关系
    RDD实现了基于Lineage的容错机制
  • 依赖关系
    宽依赖
    窄依赖
  • spark原理及应用 spark详解_System_06

  • 宽依赖对比窄依赖
    宽依赖对应shuffle操作,需要在运行时将同一个父RDD的分区传入到不同的子RDD分区中,不同的分区可能位于不同的节点,就可能设计多个节点间的数据传输
    当RDD分区丢失时,Spark会对数据进行重新计算,对于窄依赖只需要重新计算一次子RDD的父RDD分区。
    结论:相比于宽依赖,窄依赖对优化更有利
  • spark原理及应用 spark详解_spark原理及应用_07

DAG的工作原理

  • 根据RDD之间的依赖关系,形成一个DAG(有向无环图)
  • DAGScheduler将DAG划分为多个Stage
    划分依据:是否发生宽依赖(shuffle)
    划分规则:从后往前,遇到宽依赖切割为新的Stage
    每个Stage由一组并行的Task组成

    为什么需要划分Stage?
    数据本地化
    移动计算,而不是移动数据
    保证一个Stage内不会发生数据移动

最佳实践:

尽量避免shuffle

提前部分聚合减少数据移动

spark原理及应用 spark详解_System_08

Spark Shuffle过程

  • 在分区之间重新分配数据
    父RDD中同一分区中的数据按照算子要求重新进入子RDD的不同分区中
    中间结果写入磁盘
    由子RDD拉取数据,而不是由父RDD推送
    默认情况下,shuffle不会改变分区数量

spark原理及应用 spark详解_apache_09

RDD优化

  • RDD持久化
  • RDD共享变量
  • RDD分区设计
  • 数据倾斜

RDD持久化

  • RDD缓存机制:缓存数据至内存/磁盘,可大幅度提升spark应用性能
    cache=persisit(MEMORY)
    persist
  • 缓存策略StorageLevel
    MEMORY_ONLY(默认)
    MEMORY_AND_DISK
    DISK_ONLY
  • 缓存应用场景:
    从文件加载数据之后,因为重新获取文件成本较高,经过较多的算子变换之后,重新计算成本较高;
    单个非常消耗资源的算子之后;
  • 使用注意事项:
    cache()或persist()后不能再由其他算子
    cache()或persisit()遇到action算子完成后才生效

检查点

类似于快照
setCheckpointDir(“hdfs:/checkpoint0918”)
检查点与缓存的区别:
检查点会删除RDD lineage,而缓存不会
Spark Context被销毁后,检查点数据不会被删除

共享变量

广播变量:允许开发者将一个只读变量driver端缓存到每个节点Executor上,而不是每个任务传递一个副本
sc.broadcase();
注意事项:

  • Driver端变量在每个Executor每个Task保存一个变量副本
  • Driver端广播变量在每个Executor只保存一个变量副本

分区设计

  • 分区大小限制为2GB
  • 分区太少不利于并发,更容易受数据倾斜影响
  • groupby,reduceByKey,sortByKey等内存压力增大

数据倾斜

指分区中的数据分配不均匀,数据集中在少数分区中,严重影响性能通常发生在groupby,join等之后

spark原理及应用 spark详解_apache_10


解决方案:使用新的hash值(如对key加盐)重新分区

装载数据源

Spark SQL

  • Spark sql是把spark sql转换成RDD,然后提交到集群执行,执行效率非常快;同时spark sql也支持从hive中读取数据。

Spark SQL架构

  • Spark SQL是Spark的核心组件之一
  • 能够直接访问现存的Hive数据
  • 提供JDBC/ODBC接口供第三方工具借助Spark进行数据处理
  • 提供了更高层级的接口方便的处理数据
  • 支持多种操作方式:sql,API编程
  • 支持多种外部数据源:Rarquet,JSON,RDBMS等

Spark SQL运行原理

Catalyst优化器是Spark SQL的核心

spark原理及应用 spark详解_apache_11


Catalyst Optimizer:Catalyst优化器,将逻辑计划转为物理计划

优化器

  • 逻辑计划
  • 优化
  • 物理计划

SparkSQL API

  • SparkContext
  • SQLContext:Spark SQL的编程入口
  • HiveContext:SQLContext的子集,包含更多功能
  • SparkSession,SparkSession合并了SQLContext与HiveContext,提供与Spark功能交互单一入口点,并允许使用DataFrame和Dataset API对Spark进行编程

Dataset

特定域对象中的强类型集合

DataFrame

DataFrame=Dataset[Row]
类似传统数据的二维表格
在RDD基础上加入了Schema(数据结构信息)
DataFrame Schema支持嵌套数据类型:struct、map、array
提供更多类似SQL操作的API

SparkSQL操作外部数据源

SparkSQL支持的外部数据源

spark原理及应用 spark详解_apache_12

Parquet文件

Parquet文件是一种流行的列式存储格式,以二进制存储,文件中包含数据与元数据

Hive表

SparkSQL与Hive的集成

  • hive-site.xml拷贝至${SPARK_HOME}/conf下
  • 检查hive.metastore.uris是否正确
  • 启动元数据服务:$hive service metastore
  • 自行创建SparkSession,应用配置仓库地址与启用hive支持

RDBMS表

SparkSQL函数

内置函数

spark原理及应用 spark详解_System_13

自定义函数

Spark性能优化

序列化

  • Java序列化,Spark的默认序列化方式
  • Kryo序列化,比Java序列化快约10倍,但不支持所有可序列化类型
  • 使用对象数组、原始类型代替Java、Scala集合类
  • 避免嵌套结构
  • 尽量使用数字作为Key,而非字符串
  • 以较大的RDD使用MEMORY_ONLY_SER
  • 加载CSV、JSON时,仅加载所需字段
  • 仅在需要时持久化中间结果(RDD/DS/DF)
  • 避免不必要的中间结果(RDD/DS/DF)的生成
  • DF的执行速度比DS快约3倍
  • 自定义RDD分区与spark.default.parallelism,该参数用于设置每个stage的默认task数量
  • 将大变量广播出去,而不是直接使用
  • 尝试处理本地数据并最小化跨工作节点的数据传输
  • 表连接(join操作),包含所有的谓词(predicate),最大的表放在第一位,广播最小的表,最小化表join的数量

SparkGraphX图形数据分析

为什么需要图计算:

  • 许多大数据以大规模图或网络的形式呈现
  • 许多非图结构的大数据,常会被转换为图模型进行分析
  • 图数据结构很好的表达了数据之间的关联性

图的基本概念

  • 图是由顶点集合vertex及顶点间饿关系集合,边edge组成的一种网状数据结构,通常表示为二元组:Gragh=(V, E),可以对事物之间的关系建模
  • 应用场景,在地图应用总找最短路径,社交网络关系,网页间超链接关系
    图的术语:
  • 顶点 Vertex
  • 边 Edge
  • spark原理及应用 spark详解_System_14

  • 有向图:
  • spark原理及应用 spark详解_apache_15

  • 无向图:
  • spark原理及应用 spark详解_apache_16

  • 有环图:包含一系列顶点连接的回路(环路)
  • 无环图:DAG即为有向无环图
  • spark原理及应用 spark详解_apache_17

  • 度:一个顶点所有边的数量,
    出度:指从当前顶点指向其他顶点的边的数量;
    入度:其他顶点指向当前顶点的边的数量;
  • spark原理及应用 spark详解_spark原理及应用_18

  • 图的经典表示方法
    邻接矩阵
  • spark原理及应用 spark详解_spark原理及应用_19

  • 1、对于每条边,矩阵中相应的单元格值为1
    2、对于每个循环,矩阵中相应单元格值为2,方便在行或列上求得顶点度数

GraphX核心抽象

弹性分布式属性图(Resilient Distributed Property Graph):

  • 顶点和边都带属性的有向多重图
  • spark原理及应用 spark详解_apache_20

  • 一份物理存储,两种视图
  • spark原理及应用 spark详解_System_21

GraphX API

  • Graph[VD,ED]
  • VertexRDD[VD]
  • EdgeRDD[ED]
  • EdgeTriplet[VD,ED]
  • Edge:样例类
  • VertexId: Long的别名

pergel函数