Spark快速大数据分析要点
1、默认情况下,Spark 的 RDD 会在你每次对它们进行行动操作时重新计算。如果想在多个行动操作中重用同一个 RDD,可以使用 RDD.persist() 让 Spark 把这个数据以序列化的形式缓存在 JVM 的堆空
间中。我们可以让 Spark 把数据持久化到许多不同的地方。在第一次对持久化的 RDD 计算之后,Spark 会把 RDD 的内容保存到内存中(以分区方式存储到集群中的各机器上),这样在之后的行动操作中,就可以重用这些数据了。 默认不进行持久化(如果不会重用该 RDD,我们就没有必要浪费存储空间)。RDD 可以使用 result.persist(StorageLevel.DISK_ONLY) 选择不同的持久化级别。 persist() 调用本身不会触发强制求值。如果要缓存的数据太多,内存中放不下,Spark 会自动利用最近最少使用(LRU)的缓存策略把最老的分区从内存中移除。对于仅把数据存放在内存中的缓存级别,下一次要用到已经被移除的分区时,这些分区就需要重新计算。但是对于使用内存与磁盘的缓存级别的分区来说,被移除的分区都会写入磁盘。RDD 还有一个方法叫作 unpersist() ,调用该方法可以手动把持久化的 RDD 从缓存中移除。
2、 ETL(抽取、转化、装载)
3、Spark 为包含键值对类型的 RDD 提供了一些专有的操作。这些 RDD 被称为 pair RDD
4、当需要把一个普通的 RDD 转为 pair RDD 时,可以调用 map() 函数来实现,传递的函数需要返回键值对:val pairs = lines.map(x => (x.split(" ")(0), x))
5、调用 reduceByKey() 和 foldByKey() 会在为每个键计算全局的总结果之前先自动在每台机器上进行本地合并。用户不需要指定合并器。更泛化的combineByKey() 接口可以让你自定义合并的行为。
6、我们希望在除分组操作和聚合操作之外的操作中也能改变 RDD 的分区。对于这样的情况,Spark 提供了 repartition() 函数。它会把数据通过网络进行混洗,并创建出新的分区集合。切记,对数据进行重新分区是代价相对比较大的操作。Spark 中也有一个优化版的 repartition() ,叫作 coalesce()
7、。在分布式程序中,通信的代价是很大的,因此控制数据分布以获得最少的网络传输可以极大地提升整体性能。和单节点的程序需要为记录集合选择合适的数据结构一样,Spark 程序可以通过控制RDD 分区方式来减少通信开销。Spark 中所有的键值对 RDD 都可以进行分区。尽管 Spark 没有给出显示控制每个键具体落在哪一个工作节点上的方法(部分原因是Spark 即使在某些节点失败时依然可以工作),但 Spark 可以确保同一组的键出现在同一个节点上
8、使用 partitionBy() 转化操作可以减少数据混洗及网络传输。使用join连接,程序开始时,对初始userDataRDD表使用 partitionBy() 转化操作,将这张表转为哈希分区。可以通过向 partitionBy 传递一个spark.HashPartitioner 对象来实现该操作。进来一批eventRdd后,当调用 userData.join(events) 时,Spark 只会对 events 进行数据混洗操作,将 events 中特定 UserID 的记录发送到 userData 的对应分区所在的那台机器上(见图 4-5)。这样,需要通过网络传输的数据就大大减少了,程序运行速度也可以显著提升了。应该对 partitionBy() 的结果进行持久化,并保存为 userData。不进行持久化会导致整个 RDD 谱系图重新求值。致重复对数据进行分区以及跨节点的混洗。
9、一个文本文件读取为 RDD 时,输入的每一行都会成为 RDD 的一个元素。也可以将多个完整的文本文件一次性读取为一个 pair RDD,其中键是文件名,值是文件内容。如果多个输入文件以一个包含数据所有部分的目录的形式出现,可以用两种方式来处理。可以仍使用 textFile 函数,传递目录作为参数,如果文件足够小,那么可以使用 SparkContext.wholeTextFiles() 方法,该方法会返回一个 pair RDD,其中键是输入文件的文件名
10,读取JSON,文件中的每一行都是一条 JSON 记录。如果你有跨行的JSON 数据,你就只能读入整个文件,然后对每个文件进行解析。
11、定义一个累加变量(全局变量):# 创建Accumulator[Int]并初始化为0: blankLines = sparkContext.longAccumulator("ErroNum"),Spark闭包里的执行器代码可以使用累加器的 += 方法(在Java中是 add )增加累加器的值.累加器是一个只写变量
12、Spark 会自动重新执行失败的或较慢的任务来应对有错误的或者比较慢的机器。例如,如果对某分区执行 map() 操作的节点失败了,Spark 会在另一个节点上重新运行该任务。即使该节点没有崩溃,而只是处理速度比别的节点慢很多,Spark 也可以抢占式地在另一个节点上启动一个“投机”(speculative)型的任务副本,如果该任务更早结束就可以直接获取结果。即使没有节点失败,Spark 有时也需要重新运行任务来获取缓存中被移除出内存的数据。因此最终结果就是同一个函数可能对同一个数据运行了多次。
13、 因为Spark 相同的Task有可能被重复执行多次(容错性导致),所以累加器的值很有可能被累加多次,那么得到的结果就不准确了,所以一般把累加器放在行动操作中来使用,只有这样,Spark才会把每个任务对各累加器的修改应用一次
14、持久化persist:要持久化一个RDD,只要调用其cache()或者persist()方法即可。在该RDD第一次被计算出来时,就会直接缓存在每个节点中。
15、共享变量——广播变量Broadcast:通常情况下,当一个RDD的很多操作都需要使用driver中定义的变量时,每次操作,d