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的垃圾回收时间。
硬件供给:
- 分配给每个执行器节点的内存大小。
- 每个执行器节点占用的核心数。
- 执行器节点总数。
- 存储临时数据的本地磁盘数量。
Spark架构允许线性伸缩,双倍资源通常能使应用的运行时间减半。
除了内存和CPU核心,Spark还用本地磁盘来存储数据混洗操作的中间数据,以及溢写到磁盘中的RDD分区数据。一般做法是在磁盘的每个分卷中都为Spark设置一个本地目录。写操作会被均衡的分配到所有提供的目录中。
执行器节点的内存越多越好吗?
不是的。使用巨大的堆空间可能会导致垃圾回收的长时间暂停,从而严重影响Spark作业的吞吐量。减轻垃圾回收的影响:
1、给单个执行器实例分配较小的内存。
2、用序列化的格式存储数据。