Spark快速大数据分析

概念

数据的两个方向:

数据科学:分析+建模(回答业务问题、挖掘潜在规律、辅助产品推荐)

数据处理:硬件(内存、集群)+软件(封装、接口、监控、优化)

框架

应用层:

SparkStreaming

SparkSql

SparkGraphx

SparkMLlib

中间层:SparkCore

分布式集群部署:Standalone(Spark自带),YARN(Hadoop自带),Mesos(Apache)

Spark界面管理端口:4040。

Hadoop MapReduce:一个周期内操作一次map-reduce。

Spark MapReduce:一个周期内操作多次map-reduce。

步骤

处理流程

sparkconf – sparkcontext - 读取文件路径(parallelize对象) - 处理rdd-输出到文件。

转化(transformation)

filter、flatmap(有对象,生成一个iterator对象,再将这个对象拍平为String类型等)、map(无对象,返回一个任意指定的类型String等)、maptopair、sortbykey。

聚合:
reduce():reduceByKey:返回与输入数据类型相同的返回值。
combine():combineByKey:和aggregate一样,可以返回与输入数据类型不同的返回值。
flod():flodByKey:使用一个数据类型相同的零值作为初始值,与另一个元素合并。
groupBykey:笛卡尔积。

行动(action)

count、collect、first、take、top、foreach。

持久化缓存(persisit)

一个rdd对应多个action。rdd以序列化/非序列化的形式缓存到内存或/和磁盘。

为什么要持久化(缓存)?
多次使用同一个RDD进行行动操作,Spark每次都会重算RDD以及它的所有依赖。

唯一性(distinct)、排序(sortbykey)、混洗(shuffle)

所有数据分区混洗shuffle带来的开销很大。

分区(partitioner)

被一起访问的数据放到同一个节点/分区。保证并行。最大分区数为集群中设定核心core的数量。

rdd分区数默认固定,可重新分区(repartition,带来数据混洗开销),分区查看rdd. partition.size。

控制RDD分区方式,减少通信开销。哈希分区(快速查找)、范围分区(数据降维)。

分区继承:rdd3=rdd1.join(rdd2)。rdd1有分区,rdd3继承rdd1的分区。rdd1无分区,rdd3继承rdd2分区。

自定义分区:继承类spark.partitioner.

分组(join + groupByKey)

根据针对键(key)的函数对数据元素进行分组。

PairRdd操作:join(笛卡尔积)、分组groupByKey、聚合combineBykey、计算reduceByKey、排序sortByKey。

Java Function

Function<T,R>/Function2<T1,T2,R>/FlatMapFunction<T,R>

匿名内部类:new Function<T,R> { return false; }。

具名类:class FlatMapBase implements FlatMapFunction<String, String> {} 顶级具名类比较清晰。

java没有自带的二元组类型,因此采用scala.Tuple2._1()/_2().

调优

Spark不提供显示控制每个键key具体落在哪一个工作节点,但是尽量确保同一组键key出现在同一个节点。

尽量避免跨节点的数据混洗shuffle操作:

避免使用map(),它改变了键值,不会继承分区方式。采用mapValues()/flatMapValues()不改变键值,继承分区方式。

pairRDD.mapValues(s -> {

return "";

}).count();

同一个函数可能对同一份数据运行多次:节点失败、节点慢(快节点投机)、内存不足(缓存移除)。

当一个action执行完,它占有的资源会释放掉。执行结果保存到driver驱动器上。

并行度:rdd.coalesce(5).cache(),可在执行过程中调整。

Spark序列化:java原生序列化, kryo序列化。kryo是java原生序列化性能十几倍。protostuff是一个基于protobuf实现的序列化方法,它较于protobuf最明显的好处是,在几乎不损耗性能的情况下做到了不用我们写.proto文件来实现序列化。

数据源

文本textFile、二进制sequenceFile-非结构化

一般直接整体进行存储,而且一般存储为二进制的数据格式。

JSON-半结构化(K-V)

同一类实体可以有不同的属性,即使他们被组合在一起,这些属性的顺序并不重要。

Protobuffer、数据库-结构化

数据以行为单位,一行数据表示一个实体的信息,每一行数据的属性是相同且固定不变。

数据存储

Cassandra

ElasticSearch

JDBC

非文件系统存储

HBase、MongoDB:

hadoopFile()、saveAsHadoopFile()

针对二进制文件的存储

PYTHON/SCALA:
pairRdd.saveAsSequenceFile(filename)
JAVA:
pairRdd.saveAsHadoopFile(filename, Text.class, IntWritable.class, SequenceFileOutputFormat.class)

任意对象存储

JAVA:
sparkContext.objectFile()
saceAsObjectFile()
PYTHON:
sparkContext.pickleFile()
saceAsPickleFile()

数据处理

JSON数据

Python:
import json
json=json.loads(str)
str=json.dumps(json)
JAVA:
ObjectMapper mapper=new ObjectMapper();
json=mapper.readValue(str, Json.class);
str=Mapper.writeValueAsString(json);

CSV/TSV数据

CSV:逗号分隔值

TSV:制表符分隔值

共享变量

累加器(accumulator只写):数值统计,可自定义。Stats()-RDD的汇总统计。

广播(broadcast只读):多次查询一个很大的固定的数据集。如果改变广播值,则只对当前节点有效,其他节点无效。

共享连接池(配置):多个分区有同样的连接配置。

共享对象的创建:不需要反复创建。

部署

集群管理器

启动(7077):sbin/start_all.sh、sbin/stop_all.sh、bin/spark-class。Master – client通过ssh连接。

提交(8080):bin/spark-submit –master xx:7077 main()。两种模式deploymode:client(主节点/驱动器为提交的机器)、cluster(集群)。

资源:执行器进程内存executor-memory、核心core总数最大值total-executorCores。

可用性:节点故障 – Apache ZooKeeper-分布式协调系统-多个备用主节点。

关键性能考量

并行度:

过低-资源闲置,过高-分区间接开销大。数据混洗时指定RDD并行度。重新分区。

序列化格式:

在数据混洗时发生,网络数据传输开销大。采用Kryo工具更短时间更高压缩比。

内存管理:

1、默认是RDD存储占60%,数据混洗产生的数据存储占20%,用户程序存储占20%。如果用户代码中分配了大量的对象,则考虑降低RDD存储和数据混洗结果存储。

2、默认缓存策略:cache()操作会以MEMORY_ONLY存储等级持久化(缓存)数据。可以采用MEMORY_AND_DISK存储等级调用persist(),内存中放不下的旧分区会被写入磁盘,比重算各分区代价低性能更稳定。
3、默认缓存策略:直接缓存对象。可以先将对象序列化后再缓存,序列化的操作代价使缓存变慢,但可以减少JVM的垃圾回收时间。

硬件供给:

  1. 分配给每个执行器节点的内存大小。
  2. 每个执行器节点占用的核心数。
  3. 执行器节点总数。
  4. 存储临时数据的本地磁盘数量。

Spark架构允许线性伸缩,双倍资源通常能使应用的运行时间减半。
除了内存和CPU核心,Spark还用本地磁盘来存储数据混洗操作的中间数据,以及溢写到磁盘中的RDD分区数据。一般做法是在磁盘的每个分卷中都为Spark设置一个本地目录。写操作会被均衡的分配到所有提供的目录中。
执行器节点的内存越多越好吗?

不是的。使用巨大的堆空间可能会导致垃圾回收的长时间暂停,从而严重影响Spark作业的吞吐量。减轻垃圾回收的影响:
1、给单个执行器实例分配较小的内存。
2、用序列化的格式存储数据。