小文件 --> (指定分区数量/文件数量)[向上取整] * 文件数量
源码粗略解析
和map类似, 遍历每一个分区, 对每一个分区进行操作, 有返回值
mapPartitions f: Iterator[T] => Iterator[U]
可以使用分区下标进行操作分区
参数列表:(f: (Int, Iterator[T]) => Iterator[U], preservesPartitioning: Boolean = false)
聚合算子, 是一个柯里化, 可以传入两个参数列表, 第一个参数列表可以传入一个默认值, 第二个参数列表传入两个函数, 在聚合时, 先将传入的默认值加入到每一个分区中, 然后每一个分区根据传入的第一个函数进行聚合, 当每个分区聚合完成之后, 在根据传入的第二个函数对分区进行聚合操作, 在这一次聚合时, 默认值也算进来.
需要注意的是, 在第二次聚合时, 由于是多个线程并行执行, 所以当字符串在第二次聚合时不分先后, 结果也是不确定地.
(zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U
和aggregate类似, 只不过aggregateByKey操作的是键值对, 对key相同的进行聚合
(zeroValue: U, partitioner: Partitioner)(seqOp: (U, V) => U, combOp: (U, U) => U): RDD[(K, U)
combineByKey: 需要三个参数, 第一个参数是创建一个Combiner的函数, 第二个参数是局部聚合函数, 第三个参数是全局聚合函数
(createCombiner: V => C, mergeValue: (C, V) => C, mergeCombiners: (C, C) => C)
这里是创建两个List集合, 然后进行拉链操作, 将两个集合合并一下. 这里做的是将个数为1的动物放在一起, 将个数为2的动物放在一起
countByKey: 对键值对进行操作, 对相同的key进行累加操作
countByValue: 对完全相同的元素进行累加求和[容易忽略]
根据输入的key的范围进行过滤, 例如输入a,d 那么会根据key的字典顺序进行筛选, 选出属于a到d范围内的元素
和flatMap类似, 这里操作的是values, 对values进行扁平化处理以及map操作, 操作完成之后将value 和对应的key组成一个元组
和foreach类似, foreachPartition是遍历每一个分区, 对每一个分区进行操作, 需要注意的是foreachPartition没有返回值, 所以在shell中看不到效果
根据传入的函数进行操作, 将返回值作为key, 和原来的值拼成一个元组
collect的返回值是一个Array, 而在调用collectAsMap后, 返回值是一个Map
repartition: 一定会发生shuffle过程, 本身不会发生重新分区, 而是会返回一个新的分区
coalesce: 默认shuffle=false(不发生shuffle), 分区变多时需要手动指定shuffle为true, 否则分区数量不会改变, 而分区变少时, 不需要刻意指定shuffle, 因为分区变少不需要发生shuffle过程
partitionBy: 参数是一个分区器, 需要自己传入一个分区器. 需要注意的是只有 k–>v形式的值 才能调用partitionBy的方法.
当某一个分区数据量比较大的时候会采取将这个分区拆分成多个分区
checkpoint可以将重要的中间结果集写入到磁盘, 方便下次直接使用, 无需再次运行, 提高效率. 可以将中间结果集放到本地磁盘或者HDFS上, 推荐放到HDFS上, HDFS多个副本, 保证数据的安全性
步骤:
spark不同版本的RPC通信框架
spark 1.3--------->actor
spark 1.3~1.5--->AKKA
spark 1.6--------->AKKA+netty
spark 2.0--------->netty
spark集群启动流程
任务提交命令
spark-submit \
--master spark//hadoop:7077 \
--executor-memory 1024g \
--total-executor-cores 2 \
.......jar args
任务提交流程
根据用户进出基站的信息以及基站的基本信息求出用户所在时间最长的两个基站id
文件1 用户进出基站信息
手机号, 时间戳, 基站id, 时间类型(1:进站, 0:出站)
根据第一个文件求出用户在每一个基站的停留时间. 然后和第二个文件进行join操作, 然后进行排序, 取出时间戳最大的前两个元素
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object MobileLocation {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("MobileLocation").setMaster("local")
val sc = new SparkContext(conf)
//获取用户访问信息
val files: RDD[String] = sc.textFile("G://06-Spark/sparkcoursesinfo/spark/data/lacduration/log")
//切分用户访问信息
val splitedUserInfo: RDD[((String, String), Long)] = files.map(line => {
val fields: Array[String] = line.split(",")
val phone: String = fields(0) //手机号
val time: Long = fields(1).toLong //时间戳
val lac: String = fields(2) //基站id
val eventType: Int = fields(3).toInt
val time_long = if (eventType == 1) -time else time //如果事件类型为1 , 将时间戳变成负的
((phone, lac), time_long)
})
// 计算用户在基站停留的总时长
val aggred: RDD[((String, String), Long)] = splitedUserInfo.reduceByKey(_ + _)
// 为了方便和基站进行join, 将数据进行调整
val lacAndPhoneAndTime: RDD[(String, (String, Long))] = aggred.map(tup => {
val phone: String = tup._1._1 // 手机号
val lac: String = tup._1._2 //基站id
val time: Long = tup._2 // 用户在某个基站停留的总时长
(lac, (phone, time))
})
// 获取基站信息
val lacInfo: RDD[String] = sc.textFile("G://06-Spark/sparkcoursesinfo/spark/data/lacduration/lac_info.txt")
// 切分基站信息
val splitedLacInfo: RDD[(String, (String, String))] = lacInfo.map(line => {
val fields: Array[String] = line.split(",")
val lac: String = fields(0) // 基站id
val x: String = fields(1) // 经度
val y: String = fields(2) // 纬度
(lac, (x, y))
})
// 用户信息和基站信息进行join
val joined: RDD[(String, ((String, Long), (String, String)))] = lacAndPhoneAndTime.join(splitedLacInfo)
val lacAndPhoenAndTimeAndXY: RDD[(String, String, Long, (String, String))] = joined.map(tup => {
val lac: String = tup._1 //基站id
val phone: String = tup._2._1._1 // 手机号
val time: Long = tup._2._1._2 // 停留时长
val xy: (String, String) = tup._2._2 // 经纬度
(lac, phone, time, xy)
})
// 为了便于统计用户在所有基站的信息 生成用户粒度的数据
val grouped: RDD[(String, Iterable[(String, String, Long, (String, String))])] = lacAndPhoenAndTimeAndXY.groupBy(_._2)
// 按照时长进行组内排序
//grouped.map(x=>(x._1,x._2.toList.sortBy(_._3).reverse))
val sorted: RDD[(String, List[(String, String, Long, (String, String))])] = grouped.mapValues(_.toList.sortBy(_._3).reverse)
// 获取top2
val res: RDD[(String, List[(String, String, Long, (String, String))])] = sorted.mapValues(_.take(2))
println(res.collect.toBuffer)
sc.stop()
}
}