RDD弹性分布式数据集,spark最基本的数据抽象,代表一个不可变,可分区,里面元素可并行计算的集合。
具有数据流模型的特点:自动容错,位置感知性调度和可伸缩性。
RDD允许用户在执行多个查询时,显示地将工作集缓存在内存中,后续的查询能重用工作集,这极大提高查询速度
特点:一系列的分区,每一个函数作用于每个分区,RDD之间是一系列依赖,如果是k-v类型的RDD,会有一个分区器,分区器就是决定把数据放到哪个分区
一般Worker需要和DataNode节点部署在一起,这样可以有效的避免大量网络IO
一个分区一定在一个节点上,一个节点可以有多个分区
RDD由多个分区组成的分布式数据集
一个block块对应一个分区
./spark-shell
--master spark://hadoop01:7077 master地址
--executor-memory 512m 内存
--total-executor-cores 2 cpu核数
./spark-submit
.
--class org.apache... 类名
--master spark://hadoop01:7077 master地址
--executor-memory 512m 内存
--total-executor-cores 2 cpu核数
jar包路径
args 其它参数(需要传才写)
1、首先启动master进程
2、master开始解析slaves配置文件,找到worker的host,然后启动相应的worker
3、worker开始与master进行注册,把注册信息发送给master
4、master收到注册信息后,把注册信息保存到内存与硬盘中,然后master给worker发送注册成功的信息(masterurl)
5、worker收到master的url信息,开始与master建立心跳
1、driver 端 的sparksubmit 进程与master进行通信,创建一个重要的对象(sparkcontext)
2、master收到任务信息后,开始资源调度,和所有的worker进行通信,找到比较空闲的worker,并通知worker启动excutor进程
3、executor进程启动后,开始与driver(client) 进行通信(反向注册),driver开始将任务提交到相应的executor,executor开始计算任务
主要说的是partition 分区
窄依赖:一父分区只能对应一子分区 分组后join
宽依赖:一父分区可以对应多子分区,宽依赖与shuffle密不可分 没分组join
RDD转换为RDD(只支持粗粒度转换),将创建RDD的一系列Lineage(即血统)记录下来,以便恢复分区
Lineage会记录元数据信息和转换行为,当RDD部分分区数据丢失时,通过Lineage保存的信息来重新运算和恢复丢失的数据分区
一系列一对一关系(窄依赖),会形成一个Pipeline。可以通过父分区来再运行计算丢失分区的数据,其它的Pipeline不受影响
Lineage在开发中不如cache 然后checkpoint(缓存放到hdfs)
丢失分区数据后找回数据顺序:cache(内存中找),checkpoint(hdfs中恢复),Lineage(从父分区再运行计算)
它与集群容错机制有点像。
集群容错机制:当一个节点(Worker)突然宕机,Master(重新调度任务)会将宕机的节点Worker未完成的数据交给其它节点。其它节点再在自己上面启动一个Worker,然后Worker启动Executor运行
cache:重要RDD的缓存起来,里面调用的是persist,默认是内存缓存(可以选择缓存级别)
checkpoint:持久化到本地或者hdfs
Spark速度快的原因:在不同操作中可以在内存中持久化或者缓存多个数据集(RDD),当持久化后,每个分区都把计算的数据分片保存在内存中,在此RDD或者之后的计算中可以重用,使后续动作更加迅速。缓存是spark构建迭代式算法和快速交互式查询的关键。
为什么要checkpoint?
运行处的中间结果,往往很重要,所以为了保证数据的安全性,要把数据做检查点
最好把checkpoint 到RDFS,便于该集群所有节点访问到
在checkpoint之前,最好先cache一下,就是先把数据缓存到内存,这样便于运行程序时调用,也便于checkpoint时直接重缓存中获取数据
什么时候做checkpoint?
在发生shuffle之后做checkpoint,(shuffle速度慢,项目中shuffle越少越好)
checkpoint的步骤?
1、创建checkpoint储存目录 sc.checkpointDir("hdfs://...")
2、把数据cache起来 rdd.cache()
3、checkpoint rdd.checkpoint()
### 注意:当数据很大时,慎用cache,也不要不用,(关键地方,数据集不是非常大就用)
rdd.unpersist() 清除cache(cache的rdd调用unpersist())
stage划分:找到最后的RDD,向前找,以宽依赖划分(宽依赖前的)为一个stage,整体划为一个stage,直到所有RDD划分完。(每个宽依赖划)
Stage:根据RDD之间的依赖关系的不同将DAG划分为不同的Stage,对于窄依赖,partition的转换处理在stage中完成计算,对于宽依赖,由于有shuffle的存在,只能在parentRDD中处理完成后才开始接下来的计算,因此宽依赖是划分stage的依据。
划分stage是为了把RDD来生成一个个task提交到Executor中执行,所以需要把RDD 先划分stage再生成task。
一个Stage 生成n个分区个task
task的生成是依据stage,在stage中先划分pipeline(与分区个数相同),然后根据pipeline生成task
shuffle read :发生在shuffle后,把父RDD 的数据读取到子RDD中
shuffle write: 把中间结果数据写到磁盘,为了保证数据安全性。
shuffle write 为什么不写内存里?
1、避免结果集太大而占用太多的内存资源,造成内存溢出
2、保存到磁盘可以保证数据的安全性
RDD的生成 : RDD 依赖关系,
stage的划分 : DAG 划分stage(在sparkcontext中 调用了 DAGScheduler(划分stage的))
任务的生成 :在sparkcontext中 调用了 TaskSetScheduler
任务的提交 :多节点提交
在sparkcontext中 调用 actorSystem,DAGScheduler,TaskSetScheduler详情看sparkcontext源码
resoucemanager 相当于master,负责任务调度,nodemanage相当于worker,负责创建容器和启动自己的子进程。
client端相当于driver,提交任务
yarnchild 相当于executor,直接参与计算进程
//这是向mysql中数据库 bigdata下的location_info表中查询数据
object JdbcRDD {
def main(args: Array[String]): Unit = {
val conf =new SparkConf().setAppName("jdbcRDD").setMaster("local[*]")
val sc =new SparkContext(conf)
val conn=()=>{
Class.forName("com.mysql.jdbc.Driver").newInstance()
DriverManager.getConnection("jdbc:mysql://192.168.216.53:3306/bigdata?useUnicode=true&characterEncoding=utf8","root","root")
}
val sql="select id,location,counts,access_date from location_info where id >= ? and id <= ? order by counts"
val jdbcRDD =new JdbcRDD(
sc,conn,sql,0,100,2,
res=>{
val id=res.getInt("id")
val location =res.getString("location")
val counts=res.getInt("counts")
val access_date=res.getDate("access_date")
(id,location,counts,access_date)
}
)
println(jdbcRDD.collect().toBuffer)
sc.stop()
}
}
第一种有隐式转换,第二种就直接继承
object Mysort {
implicit val girlOrdering = new Ordering[Girl] {
override def compare(x: Girl, y: Girl) = {
if (x.faceValue != y.faceValue) {
x.faceValue - y.faceValue
} else {
y.age - x.age
}
}
}
}
//case class Girl(val faceValue: Int, val age: Int) {}//第一种
case class Girl(val faceValue: Int, val age: Int) extends Ordered[Girl] {
override def compare(that: Girl) = {
if (this.faceValue != that.faceValue) {
this.faceValue - that.faceValue
} else {
this.age - that.age
}
}
}
object CustimSort {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("jdbcRDD").setMaster("local[*]")
val sc = new SparkContext(conf)
val girlInfo = sc.parallelize(Array(("a", 80, 25), ("b", 84, 24), ("c", 85, 26)))
//第一种排序方式
// import Mysort.girlOrdering
// val res: RDD[(String, Int, Int)] = girlInfo.sortBy(x=>Girl(x._2,x._3),false)
//第二种
val res: RDD[(String, Int, Int)] = girlInfo.sortBy(x => Girl(x._2, x._3), false)
println(res.collect().toBuffer)
sc.stop()
}
}
task 只能对accumulator进行累加
spark提供的accumulator,主要用于多个节点对一个变量进行共享操作。
它提供了多个task对一个变量并行的操作功能,task只能对Accumutor进行累加的操作,不能读取其值
只有driver才能读取。
/**
* 累加器,就是累加操作
*/
object Accumulator {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("jdbcRDD").setMaster("local[*]")
val sc = new SparkContext(conf)
val sum: Accumulator[Int] = sc.accumulator(0)
val numbers = sc.parallelize(Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 0), 2)
numbers.foreach(num => {
sum += num
})
println(sum)
sc.stop()
}
}