使用spark开发已有几个月 。相比于python/hive , scala/spark学习门槛较高 。尤其记得刚开时 , 举步维艰 , 进展十分缓慢 。不过谢天谢地 , 这段苦涩(bi)的日子过去了 。忆苦思甜 , 为了避免项目组的其他同学走弯路 , 决定总结和梳理spark的使用经验 。
1 Spark基础 基石RDD spark的核心是RDD(弹性分布式数据集) , 一种通用的数据抽象 , 封装了基础的数据操作 , 如map , filter , reduce等 。RDD提供数据共享的抽象 , 相比其他大数据处理框架 , 如MapReduce , Pegel , DryadLINQ和HIVE等均缺乏此特性 , 所以RDD更为通用 。简要地概括RDD:RDD是一个不可修改的 , 分布的对象集合 。每个RDD由多个分区组成 , 每个分区可以同时在集群中的不同节点上计算 。RDD可以包含Python , Java和Scala中的任意对象 。
2 Spark生态圈中应用都是基于RDD构建(下图) , 这一点充分说明RDD的抽象足够通用 , 可以描述大多数应用场景 。RDD操作类型—转换和动作 RDD的操作主要分两类:转换(transformation)和动作(action) 。两类函数的主要区别是 , 转换接受RDD并返回RDD , 而动作接受RDD但是返回非RDD 。转换采用惰性调用机制 , 每个RDD记录父RDD转换的方法 , 这种调用链表称之为血缘(lineage);而动作调用会直接计算 。采用惰性调用 , 通过血缘连接的RDD操作可以管道化(pipeline) , 管道化的操作可以直接在单节点完成 , 避免多次转换操作之间数据同步的等待 。使用血缘串联的操作可以保持每次计算相对简单 , 而不用担心有过多的中间数据 , 因为这些血缘操作都管道化了 , 这样也保证了逻辑的单一性 , 而不用像MapReduce那样 , 为了竟可能的减少map reduce过程 , 在单个map reduce中写入过多复杂的逻辑 。
3 RDD使用模式 RDD使用具有一般的模式 , 可以抽象为下面的几步 加载外部数据 , 创建RDD对象 使用转换(如filter) , 创建新的RDD对象 缓存需要重用的RDD 使用动作(如count) , 启动并行计算 RDD高效的策略 Spark官方提供的数据是RDD在某些场景下 , 计算效率是Hadoop的20X 。这个数据是否有水分 , 我们先不追究 , 但是RDD效率高的由一定机制保证的: RDD数据只读 , 不可修改 。如果需要修改数据 , 必须从父RDD转换(transformation)到子RDD 。所以 , 在容错策略中 , RDD没有数据冗余 , 而是通过RDD父子依赖(血缘)关系进行重算实现容错 。RDD数据在内存中 , 多个RDD操作之间 , 数据不用落地到磁盘上 , 避免不必要的I/O操作 。RDD存放的数据可以是java对象 , 所以避免的不必要的对象序列化和反序列化 。总而言之 , RDD高效的主要因素是尽量避免不必要的操作和牺牲数据的操作精度 , 用来提高计算效率 。
4 Spark使用技巧 RDD基本函数扩展 RDD虽然提供了很多函数 , 但是毕竟还是有限的 , 有时候需要扩展 , 自定义新的RDD的函数 。在spark中 , 可以通过隐式转换 , 轻松实现对RDD扩展 。画像开发过程中 , 平凡的会使用rollup操作(类似HIVE中的rollup) , 计算多个级别的聚合数据 。下面是具体实 , /** * 扩展spark rdd,为rdd提供rollup方法 */ implicit class RollupRDD[T: ClassTag](rdd: RDD[(Array[String], T)]) extends Serializable { /** * 类似Sql中的rollup操作 * * @param aggregate 聚合函数 * @param keyPlaceHold key占位符 , 默认采用FaceConf.STAT_SUMMARY * @param isCache , 确认是否缓存数据 * @return 返回聚合后的数据 */ 上面代码声明了一个隐式类 , 具有一个成员变量rdd , 类型是RDD[(Array[String], T)] , 那么如果应用代码中出现了任何这样的rdd对象 , 并且import当前的隐式转换 , 那么编译器就会将这个rdd当做上面的隐式类的对象 , 也就可以使用rollup函数 , 和一般的map , filter方法一样 。
5 RDD相关操作都需要传入自定义闭包函数(closure) , 如果这个函数需要访问外部变量 , 那么需要遵循一定的规则 , 否则会抛出运行时异常 。闭包函数传入到节点时 , 需要经过下面的步骤: 驱动程序 , 通过反射 , 运行时找到闭包访问的所有变量 , 并封成一个对象 , 然后序列化该对象 将序列化后的对象通过网络传输到worker节点 worker节点反序列化闭包对象 worker节点执行闭包函数 , 注意:外部变量在闭包内的修改不会被反馈到驱动程序 。简而言之 , 就是通过网络 , 传递函数 , 然后执行 。所以 , 被传递的变量必须可以序列化 , 否则传递失败 。本地执行时 , 仍然会执行上面四步 。广播机制也可以做到这一点 , 但是频繁的使用广播会使代码不够简洁 , 而且广播设计的初衷是将较大数据缓存到节点上 , 避免多次数据传输 , 提高计算效率 , 而不是用于进行外部变量访问 。
好好加油
【spark 缓存,sparksql缓存表】设置挺好
推荐阅读
- 经期是不是不能喝绿茶,喝绿茶对经期有影响吗
- 奥奇传说奥币怎么刷,奥拉星怎么获得
- 英国大学音乐专业全面解析课程,英国音乐类大学
- 蛋挞底部烤不熟很软
- 质子质量为多少,为何质子质量大电子质量小
- 农村新生儿医保卡在哪里办
- 哲理句子精辟励志 深奥励志的哲理的句子
- 她扒拉我是什么梗 她扒拉我是什么梗意思
- 眉毛雨衣是什么