一、本质
Spark是一个分布式的计算框架,是下一代的MapReduce,扩展了MR的数据处理流程
二、mapreduce有什么问题
1.调度慢,启动map、reduce太耗时
2.计算慢,每一步都要保存中间结果落磁盘
3.API抽象简单,只有map和reduce两个原语
4.缺乏作业流描述,一项任务需要多轮mr
三、spark解决了什么问题
1.最大化利用内存cache
2.中间结果放内存,加速迭代
3.将结果集放内存,加速后续查询和处理,解决运行慢的问题
select * from table where col1 > 50
rdd.registerastable(cachetable)
SQL:
select col2, max (col3) from cachetable group by col2
select col3, max (col2) from cachetable group by col3
- 更丰富的API(Transformation类和Actions类)
- 完整作业描述,将用户的整个作业串起来
val file = sc.textFile(hdfs://input)
val counts = file.flatMap(
line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_ + _)
counts.saveAsTextFile(hdfs://output)
- 由于Excutor进程可以运行多个Task线程,因而实现了多线程的操作,加快了处理速度
四、Spark核心—RDD( Resilient Distributed Dataset 弹性分布式数据集模型)
1.四个特征
– RDD使用户能够显式将计算结果保存在内存中,控制数据的划分
– 记录数据的变换和描述,而不是数据本身,以保证容错
– 懒操作,延迟计算,action的时候才操作
– 瞬时性,用时才产生,用完就释放
2.四种构建方法
– 从共享文件系统中获取,如从HDFS中读数据构建RDD
• val a = sc.textFile(“/xxx/yyy/file”)
– 通过现有RDD转换得到
• val b = a.map(x => (x, 1))
– 定义一个scala数组
• val c = sc.parallelize(1 to 10, 1)
– 由一个已经存在的RDD通过持久化操作生成
• val d = a.persist(), a. saveAsHadoopFile(“/xxx/yyy/zzz”)
3.partition和依赖
– 每个RDD包含了数据分块/分区(partition)的集合,每个partition是不可分割的
– 每个partition的计算就是一个task,task是调度的基本单位
– 与父RDD的依赖关系(rddA=>rddB)
宽依赖: B的每个partition依赖于A的所有partition
• 比如groupByKey、reduceByKey、join……,由A产生B时会先对A做shuffle分桶
窄依赖: B的每个partition依赖于A的常数个partition
• 比如map、filter、union……
4.stage和依赖
– 从后往前,将宽依赖的边删掉,大数据培训连通分量及其在原图中所有依赖的RDD,构成一个stage
– 每个stage内部尽可能多地包含一组具有窄依赖关系的转换,并将它们流水线并行化
5.数据局部性原则
– 如果一个任务需要的数据在某个节点的内存中,这个任务就会被分配至那个节点
– 需要的数据在某个节点的文件系统中,就分配至那个节点
6.容错性原则
– 如果此task失败,AM会重新分配task
– 如果task依赖的上层partition数据已经失效了,会先将其依赖的partition计算任务再重算一遍
• 宽依赖中被依赖partition,可以将数据保存HDFS,以便快速重构(checkpoint)
• 窄依赖只依赖上层一个partition,恢复代价较少
– 可以指定保存一个RDD的数据至节点的cache中,如果内存不够,会LRU释放一部分,仍有重构的可能
五、Spark系统架构
1.Excutor的内存分为三块:
1)task执行代码所需的内存,占总内存的20%;
2)task通过shuffle过程拉取上一个stage的task的输出后,进行聚合操作时使用,占20%
3)让RDD持久化时使用,默认占executor总内存的60%
2.Excutor的cpu core:
每个core同一时间只能执行一个线程
六、Spark资源参数和开发调优
1.七个参数
• num-executors:该作业总共需要多少executor进程执行
建议:每个作业运行一般设置5-~100个左右较合适
• executor-memory:设置每个executor进程的内存, num-executors* executor-memory代表作业申请的总内存量(尽量不要超过最大总内存的1/3~1/2)
建议:设置4G~8G较合适
• executor-cores: 每个executor进程的CPU Core数量,该参数决定每个executor进程并行执行task线程的能力,num-executors * executor-cores代表作业申请总CPU core数(不要超过总CPU Core的1/3~1/2 )
建议:设置2~4个较合适
• driver-memory: 设置Driver进程的内存
建议:通常不用设置,一般1G就够了,若出现使用collect算子将RDD数据全部拉取到Driver上处理,就必须确保该值足够大,否则OOM内存溢出
• spark.default.parallelism: 每个stage的默认task数量
建议:设置500~1000较合适,默认一个HDFS的block对应一个task,Spark默认值偏少,这样导致不能充分利用资源
• spark.storage.memoryFraction: 设置RDD持久化数据在executor内存中能占的比例,默认0.6,即默认executor 60%的内存可以保存持久化RDD数据
建议:若有较多的持久化操作,可以设置高些,超出内存的会频繁gc导致运行缓慢
• spark.shuffle.memoryFraction: 聚合操作占executor内存的比例,默认0.2
建议:若持久化操作较少,但shuffle较多时,可以降低持久化内存占比,提高shuffle操作内存占比
spark-submit:
2.六个原则
• 避免创建重复的RDD
• 尽可能复用同一个RDD
• 对多次使用的RDD进行持久化处理
• 避免使用shuffle类算子
如:groupByKey、reduceByKey、join等
• 使用map-side预聚合的shuffle操作
一定要使用shuffle的,无法用map类算子替代的,那么尽量使用map-site预聚合的算子,如可能的情况下使用reduceByKey或aggregateByKey算子替代groupByKey算子
• 使用Kryo优化序列化性能
Kryo是一个序列化类库,来优化序列化和反序列化性能, Spark支持使用Kryo序列化库,性能比Java序列化库高10倍左右
七、Spark技术栈
• Spark Core: 基于RDD提供操作接口,利用DAG进行统一的任务规划
• Spark SQL: Hive的表 + Spark的里。通过把Hive的HQL转化为Spark DAG计算来实现
• Spark Streaming: Spark的流式计算框架,延迟在1S左右,mini batch的处理方法
• MLIB: Spark的机器学习库,包含常用的机器学习算法
• GraphX: Spark图并行操作库