每一个 spark 应用程序都包含一个驱动程序(driver program),会运行用户的 main 函数,并在集群上执行各种并行操作(parallel operations)
spark导图.xmind
五个特征
(1)遇到action算子触发作业,transformation算子是不会立即触发作业提交();
(2)stage的划分,划分的依据是依赖算子是否是shuffle(如reduceByKey,Join等)的,每个stage又可以划分成若干task;这个可以这么看,没有shuffle之前都是map操作,不怎么涉及节点之间的通信;
(3)分区(Partition),影响你的并发和计算效率,比如,1000个并发,200个Partition,另外800个并发就空闲着,所以合理的设置repartition和shuffle.partitions
RDD的分区数决定这个RDD被分成多少片(partition来执行)一个片有个进程
Spark:任务中如何确定spark分区数、task数目、core个数、worker节点个数、excutor数量
eg.
假设有一个10台机器的集群,每台机器有8个逻辑核,并按照如上的配置,那么这个spark集群的可用资源是 80个core(这里只考虑cpu,实际上还有内存)。如果一个任务申请到了集群的所有资源(所有80个core)。现在有一个被分为100个partition的RDD被map执行,那么会同时启动80个Task也就是占用了所有80个core计算(实际是启动了80个线程),剩余20个partition等待某些task完成后继续执行。
RDD : 一个只读的,可分区的分布式数据集,这个数据集的部分或全部可以缓存到到内存中,在多次计算中
重复使用。(弹性分布式数据集),RDD默认不存储数据,只存储业务逻辑
RDD内部可以有许多分区(partitions),每个分区都拥有大量的记录(records)。(RDD由一组partition组成),分布在集群的不同节点上,可以并行操作
RDD 在抽象
RDD分区的三种方式
分区的优势:增加并行度和减少通信开销
分区太多意味着任务数太多,调度任务消耗时间,会增加耗时和浪费资源
分区太少,会导致部分节点分配不到任务,一些节点处理数据量会增大,对节点内存的要求高
分区不合理会导致数据倾斜问题。
一般合理的分区数是总核数的2-3倍
//确定分区的方式 partition = key.hashCode () % numPartitions
val counts = sc.parallerlize(list(1,a),(2,b),(3,c))
.partitonBy(new Hashpartitoner(3))
// 会对key值进行排序,然后将key值被划分成n份key值集合
val counts = sc.parallerlize(list((1,a),(2,b),(3,c))
.partitionBy(new RangePartitioner(3,counts))
//根据自定义来划分,需要继承一个Partitoner
package shujia.mytest.testtoperator
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD.rddToPairRDDFunctions
import org.apache.spark.Partitioner
/*
* @program: SparkScalaOperator
* @Date: 2018/9/2 19:48
* @Author: yqq
* @Description: 该函数根据partitioner函数生成新的ShuffleRDD,将原RDD重新分区
*/
object test_partionby {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("JoinOperator")
.setMaster("local")
val sc = new SparkContext(conf)
val nameList = Array(Tuple2(1,"xuruyun"),Tuple2(2,"liangyongqi"),Tuple2(3,"wangfei"),(4,"sq"))
val name = sc.parallelize(nameList)
val result = name.partitionBy(new UDPartitioner(2))//不能小于3 自定义划分了3个区
result.foreach(println)
}
}
//输入参数分区的数目
class UDPartitioner(numParts: Int) extends Partitioner {
override def numPartitions = numParts //分区的数目
override def getPartition(key: Any): Int = {
//key.hashCode() % numPartitions hash 分区的方法
if (key == 1){
1 //分区的标记必须从0开始(第一位不一定为0)
} else if (key == 2 ) {
0
}else{
1
}
}
}
RDD:创建的几种方式(scala和java)
在RDD中,通常就代表和包含了Spark应用程序的输入源数据。
当我们,在创建了初始的RDD之后,才可以通过Spark Core提供的transformation算子,对该RDD进行transformation(转换)操作,来获取其他的RDD。
Spark Core为我们提供了三种创建RDD的方式,包括:
应用场景
如果要通过并行化集合来创建RDD,需要针对程序中的集合,调用 SparkContext 中的 parallelize() 方法。Spark会将集合中的数据拷贝到集群上去,形成一个分布式的数据集合,也就是一个RDD。即:集合中的部分数据会到一个节点上,而另一部分数据会到其它节点上。然后就可以采用并行的方式来操作这个分布式数据集合。
// 并行化创建RDD部分代码
// 实现1到5的累加求和
val arr = Array(1,2,3,4,5)
val rdd = sc.parallelize(arr)
val sum = rdd.reduce(_ + _)
官网的注意点
通过阅读Spark的官方文档,如下图:
我们可知:
在调用parallelize()方法时,有一个重要的参数可以指定,就是要将集合切分成多少个partition。Spark会为每一个partition运行一个task来进行处理。Spark官方的建议是,为集群中的每个CPU创建2-4个partition。Spark默认会根据集群的情况来设置partition的数量。但是也可以在调用parallelize()方法时,传入第二个参数,来设置RDD的partition数量。比如,parallelize(arr, 10)
Spark是支持使用任何Hadoop支持的存储系统上的文件创建RDD的,比如说HDFS、Cassandra、HBase以及本地文件。通过调用SparkContext的textFile()方法,可以针对本地文件或HDFS文件创建RDD。Spark是支持使用任何Hadoop支持的存储系统上的文件创建RDD的,比如说HDFS、Cassandra、HBase以及本地文件。通过调用SparkContext的textFile()方法,可以针对本地文件或HDFS文件创建RDD。
2 通过本地文件或HDFS创建RDD的几个注意点
Spark 支持使用任何 Hadoop 支持的存储系统上的文件(HDFS,Cassandra,HBase 或者 本地文件)创建。通过调用 SparkContext 的 textFile() 方法
// 实现文件字数统计
// textFile()方法中,输入本地文件路径或是HDFS路径
// HDFS:hdfs://spark1:9000/data.txt
// local:/home/hadoop/data.txt
val rdd = sc.textFile(“/home/hadoop/data.txt”)
val wordCount = rdd.map(line => line.length).reduce(_ + _)
注意点
3 Spark 支持的其余方法,创建RDD
通过阅读Spark的官方文档,可以知道除了通过使用textFile()方法创建RDD之外,还有几个其余的方法适用于其它的应用场景,如下图:
SparkContext的textFile()除了可以针对上述几种普通的文件创建RDD之外,还有一些特例的方法来创建RDD:
sqlcontext sql上下文对象
.read.json 读取json文件 读取的文件是DF格式
将文件注册成临时表 .registerTempTable
//建立样例类。按照样例类来读取文件。(读取文件是RDD形式).DF变成DF形式并可以用sql
//来读取
.map(x => Score(x(0),x(1),x(2).toInt))
case class Score(student_id: String, coure_id : String,score:Int)
或者
//1 对数据的处理
.map(x => Row(x(0),x(1),x(2).toInt))
//2.1 创建列描述的集合
var structFields = new ArrayList[StructField]()
// 2.2列描述 列名 类型 是否为空
structFields.add(DataTypes.createStructField("student_id",DataTypes.StringType,true))
structFields.add(DataTypes.createStructField("student_subject",DataTypes.StringType,true))
structFields.add(DataTypes.createStructField("student_score",DataTypes.IntegerType,true))
//2.3创建列描述
var schema = DataTypes.createStructType(structFields)
//3 用sqlcontext创建DF
sqlcontetx.createDataframe(scoreRDD,shema)
由一个已经存在的scala集合的转换
val rdd = sc.parallelize(集合或者数组)
val a1 = sc.parallelize*(List((*1,37*)*,*(*2,57*)*,*(*3,27*)))*
sql语句只能是有sqlcontext.sql(hivecontext)能执行(DF数据可以通过sql来运行实际上底层是转换为RDD格式来处理数据。.sql 可以通过sql语句操作 因为sql语句是懒执行算子
.show查询数据 。collect是从远程集群拉取数据到本地driver端,经过网络传输,如果数据大会对
网络压力带来很大。foreach是在集群上操作
读取文件操作:本地文件 SparkContext:sc.textFile() hdfs文件 Sqlcontetx :sql.read.parquet
https://blog.csdn.net/qq_43688472/article/details/86491720
从 spark2.0 开始 DataFrame 作为 DataSet 的特例 ( DataFrame 是操作 Row 对象的 DataSet (DataFrame=DataSet[Row]) ,dataset中每一行类型是不一定的
DataSet:分布式的数据集合,Dataset 提供了强类型支持(在RDD的每行数据加了类型约束)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-chH15n1d-1596074385708)(C:\Users\12067\AppData\Roaming\Typora\typora-user-images\1586338371104.png)]
DataFrame 中提供了详细的数据结构信息即为 schema (数据集中包含哪些列,列名称,列类型)
structField、structType、schame
structField
case class StructField(
name: String,
dataType: DataType,
nullable: Boolean = true,
metadata: Metadata = Metadata.empty) {}
包含 (name ; dataType ; nullable 是否可以为空 ; metadata 此字段的元数据,如果不修改列的内容,则在转换期间应保存元数据。
/**
* StructField 是 一个 case class ,其中是否可以为空,默认是 true,初始元信息是为空
* 它是作为描述 StructType中的一个字段
*/
val sf = new StructField("b",IntegerType)
println(sf.name)//b
println(sf.dataType)//IntegerType
println(sf.nullable)//true
println(sf.metadata)//{}
structType
StructType(fields: Seq[StructField])
一个 StructType 对象,可以有多个 StructField ,同时也可以用名字(name)来提取,相当于 Map ,可以用key来提取value,但是他StructType提取的是整条字段的信息
在源码中structType是一个case class,如下:
case class StructType(fields: Array[StructField]) extends DataType with Seq[StructField] {}
Schema
数据的数据结构描述(比如描述一个Json文件),它可以是在运行的时候隐式导入,或者在编译的时候就导入。它是用一个StructField集合对象的StructType描述(用一个三元tuple,内部是:name,type.nullability),本来有四个信息的为什么会说是三元数组?其实metadata,你是可以调出来。
import org.apache.spark.sql.types._
val struct = StructType(
StructField("a",IntegerType)::
StructField("b", LongType, false) ::
StructField("c", BooleanType, false) :: Nil)
val schemaTyped = new StructType()
.add("a","int").add("b","string")
schemaTyped.foreach(println)
/**
StructField(a,IntegerType,true)
StructField(b,StringType,true)
*/
ps.参考
Spark RDD、DataFrame、Dataset相互转换
构建 RDD[Row]
构建schema
调用creatDataFrame方法
import org.apache.spark.storage.StorageLevel
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.{DataFrame, Row, SQLContext, SparkSession}
import org.apache.spark.sql.types.{IntegerType, LongType, StringType, StructField, StructType}
object spark_test1 {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("spark_test1").master("local").getOrCreate()
// spark是sparkSession对象
import spark.implicits._
val sc = spark.sparkContext
//读取文件生成RDD
val rdd = sc.textFile("file:///d:/data/words.txt")
.persist(StorageLevel.MEMORY_ONLY)//持久化级别
//1 构建RDD[Row],将一行数据放入Row
val rdd2 = rdd.flatMap(_.split(",")).map(t => {Row(t(0),t(1))
})
//2 构建schema
val schema = StructType{
List(
StructField("id",LongType,true),
StructField("user",StringType,true)
)
}
//3 createDataFrame
val df = spark.createDataFrame(rdd2,schema)
}
}
将一行数据放入元组()中,toDF()中指定字段名,需要导入隐式转换
object RddToDataFrame {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("RddToDataFrame").master("local").getOrCreate()
val rdd = spark.sparkContext.textFile("file:///d:/data/words.txt")
// 导入隐式转换
import spark.implicits._
val df = rdd.map{
x => {
val tmp = x.split(",")
(tmp(0).toInt, tmp(1))
}
}.toDF("id","name")
spark.stop()
}
跟自动推断差不多,不过需要创建一个case类,定义类属性。Spark通过反射将case类属性映射成Table表结构,字段名已经通过反射获取。需要导入隐式转换。
// case类
case class Words(id:Long,name:String) extends Serializable
object RddToDataFrame {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("RddToDataFrame").master("local").getOrCreate()
val rdd = spark.sparkContext.textFile("file:///d:/data/words.txt")
import spark.implicits._
val df = rdd.map{
x => {
val tmp = x.split(",")
Words(tmp(0).toInt, tmp(1))
}
}.toDF()
spark.stop()
}
}
def main(args: Array[String]): Unit = {
val sparkSession: SparkSession = SparkSession.builder().appName("DataFrameTest").master("local[2]").getOrCreate()
/**
* 以下是比较常用的直接从数据源生成DataFrame类型的数据
*/
sparkSession.read.json("")
sparkSession.read.table("")
sparkSession.read.text("")
sparkSession.read.jdbc()
sparkSession.read.load("")
sparkSession.read.csv("")
sparkSession.read.orc("")
sparkSession.read.parquet("")
}
object spark_test2 {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("spark_test1").master("local").getOrCreate()
// spark是sparkSession对象
import spark.implicits._
val sc = spark.sparkContext
//读取文件生成RDD
val rdd = sc.textFile("file:///d:/data/words.txt")
.persist(StorageLevel.MEMORY_ONLY) //持久化级别
//1 构建RDD[Row],将一行数据放入Row
val rdd2 = rdd.flatMap(_.split(",")).map(t => {
Row(t(0), t(1))
})
//2 构建schema
val schema = StructType {
List(
StructField("id", LongType, true),
StructField("user", StringType, true)
)
}
//3 createDataFrame
val df = spark.createDataFrame(rdd2, schema)
//4 将dataframe转换为dataset
val wordDS = df.as[word]
//5 将dataset 转成dataframe
val df1=wordDS.toDF()
df1.show()
}
case class word(id: Long,user:String)
}
spark 2.x 之后 Scala API中Dataframe只是DataSet[Row] 类型的别名,在转DataSet不用指定Row类型
代码: D:\mlsql_1\sparkHbase
package info
import org.apache.spark.sql.SparkSession
/*
* @program: sparkHbase
* @Date: 2020/5/13 11:17
* @Author: yqq
* @Description:
*/
object RDDtoDataSet {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("RddToDataset").master("local").getOrCreate()
val rdd = spark.sparkContext.textFile("file:///d:/data/words.txt")
import spark.implicits._
val rdd2 = rdd.map(_.split(",")).map(x => (x(0),x(1)))
val ds = spark.createDataset(rdd2)
spark.stop()
}
}
package info
import org.apache.spark.sql.SparkSession
/*
* @program: sparkHbase
* @Date: 2020/5/13 11:17
* @Author: yqq
* @Description:
*/
object RDDtoDataSet {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("RddToDataset").master("local").getOrCreate()
val rdd = spark.sparkContext.textFile("file:///d:/data/words.txt")
import spark.implicits._
val rdd2 = rdd.map(_.split(",")).map(x => (x(0).toInt,x(1)))
//val ds = spark.createDataset(rdd2)
rdd2.toDS()
spark.stop()
}
}
字段名已经通过反射获取
package info
import org.apache.spark.sql.SparkSession
/*
* @program: sparkHbase
* @Date: 2020/5/13 11:17
* @Author: yqq
* @Description:
*/
object RDDtoDataSet {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("RddToDataset").master("local").getOrCreate()
val rdd = spark.sparkContext.textFile("file:///d:/data/words.txt")
import spark.implicits._
val rdd2 = rdd.map(_.split(",")).map(x => words(x(0).toInt,x(1)))
//val ds = spark.createDataset(rdd2)
rdd2.toDS()
spark.stop()
}
case class words(id: Int,name: String)
}
package info
import org.apache.spark.sql.types.{LongType, StringType, StructField, StructType}
import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.storage.StorageLevel
import org.apache.spark.sql.DataFrame
/*
* @program: sparkHbase
* @Date: 2020/4/14 23:02
* @Author: yqq
* @Description:
*/
object spark_test2 {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("spark_test1").master("local").getOrCreate()
// spark是sparkSession对象
import spark.implicits._
val sc = spark.sparkContext
//读取文件生成RDD
val rdd = sc.textFile("file:///d:/data/words.txt")
.persist(StorageLevel.MEMORY_ONLY) //持久化级别
//1 构建RDD[Row],将一行数据放入Row
val rdd2 = rdd.flatMap(_.split(",")).map(t => {
Row(t(0), t(1))
})
//2 构建schema
val schema = StructType {
List(
StructField("id", LongType, true),
StructField("user", StringType, true)
)
}
//3 createDataFrame
val df = spark.createDataFrame(rdd2, schema)
//4 将dataframe转换为dataset
//4 将dataframe转换为dataset
val wordDS:Dataset[word] = df.as[word]
val wordDS1df:Dataset[Row] = df.as[Row]
//5 将dataset 转成dataframe
val df1=wordDS.toDF()
// df1.show()
}
case class word(id: Long,user:String)
}
StorageLevel.MEMORY_ONLY) //持久化级别
//1 构建RDD[Row],将一行数据放入Row
val rdd2 = rdd.flatMap(_.split(",")).map(t => {
Row(t(0), t(1))
})
//2 构建schema
val schema = StructType {
List(
StructField("id", LongType, true),
StructField("user", StringType, true)
)
}
//3 createDataFrame
val df = spark.createDataFrame(rdd2, schema)
//4 将dataframe转换为dataset
//4 将dataframe转换为dataset
val wordDS:Dataset[word] = df.as[word]
val wordDS1df:Dataset[Row] = df.as[Row]
//5 将dataset 转成dataframe
val df1=wordDS.toDF()
// df1.show()
}
case class word(id: Long,user:String)
}