关于RDD、DataFrame和Dstream的几个常识(补充 DataSet)的笔记

RDD

Spark 编程

每一个 spark 应用程序都包含一个驱动程序(driver program),会运行用户的 main 函数,并在集群上执行各种并行操作(parallel operations)

spark导图.xmind

RDD的基础知识

五个特征

  1. a list of partiotioner有很多个partiotioner(这里有3个partiotioner)。可以明确的说,一个分区在一台机器上,一个分区其实就是放在一台机器的内存上,一台机器上可以有多个分区。
  2. a function for partiotioner一个函数作用在一个分区上。比如说一个分区有1,2,3 在rdd1.map(10),把RDD里面的每一个元素取出来乘以10,每个分片都应用这个map的函数。
  3. RDD之间有一系列的依赖rdd1.map(*10).flatMap(…).map(…).reduceByKey(…),构建成为DAG,这个DAG会构造成很多个阶段,这些阶段叫做stage,RDDstage之间会有依赖关系,后面根据前面的依赖关系来构建,如果前面的数据丢了,它会记住前面的依赖,从前面进行重新恢复。每一个算子都会产生新的RDD。textFile 与flatMap会产生两个RDD。
  4. 分区器hash & Integer.Max % partiotioner 决定数据到哪个分区里面,可选,这个RDD是key-value 的时候才能有。
  5. 最佳位置。数据在哪台机器上,任务就启在哪个机器上,数据在本地上,不用走网络。不过数据进行最后汇总的时候就要走网络。(hdfs file的block块)。

(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倍

  • HashPartition (哈希分区划分器)
//确定分区的方式 partition = key.hashCode () % numPartitions
val counts = sc.parallerlize(list(1,a),(2,b),(3,c))
	.partitonBy(new Hashpartitoner(3))
  • RangePartitioner(范围分区划分器)
// 会对key值进行排序,然后将key值被划分成n份key值集合
val counts = sc.parallerlize(list((1,a),(2,b),(3,c))
	.partitionBy(new RangePartitioner(3,counts))
  • CustomPartitioner(自定义分区器)
//根据自定义来划分,需要继承一个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文件的创建

RDD:创建的几种方式(scala和java)

在RDD中,通常就代表和包含了Spark应用程序的输入源数据。
当我们,在创建了初始的RDD之后,才可以通过Spark Core提供的transformation算子,对该RDD进行transformation(转换)操作,来获取其他的RDD。
Spark Core为我们提供了三种创建RDD的方式,包括:

  1. 使用程序中的集合创建RDD
  2. 使用本地文件创建RDD
  3. 使用HDFS文件创建RDD

应用场景

  1. 使用程序中的集合创建RDD,主要用于进行测试,可以在实际部署到集群运行之前,自己使用集合构造测试数据,来测试后面的spark应用的流程
  2. 使用本地文件创建RDD,主要用于的场景为:在本地临时性地处理一些存储了大量数据的文件
  3. 使用HDFS文件创建RDD,应该是最常用的生产环境处理方式,主要可以针对HDFS上存储的大数据,进行离线批处理操作
实际操作
1 并行化创建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的官方文档,如下图:

img

我们可知:
在调用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(_ + _)

注意点

  1. 如果是针对本地文件的话:
  • 如果是在Windows上进行本地测试,windows上有一份文件即可;
  • 如果是在Spark集群上针对Linux本地文件,那么需要将文件拷贝到所有worker节点上(就是在spark-submit上使用—master指定了master节点,使用standlone模式进行运行,而textFile()方法内仍然使用的是Linux本地文件,在这种情况下,是需要将文件拷贝到所有worker节点上的);
  1. Spark的textFile()方法支持针对目录、压缩文件以及通配符进行RDD创建
  2. Spark默认会为hdfs文件的每一个block创建一个partition,但是也可以通过textFile()的第二个参数手动设置分区数量,只能比block数量多,不能比block数量少

3 Spark 支持的其余方法,创建RDD

通过阅读Spark的官方文档,可以知道除了通过使用textFile()方法创建RDD之外,还有几个其余的方法适用于其它的应用场景,如下图:

img

SparkContext的textFile()除了可以针对上述几种普通的文件创建RDD之外,还有一些特例的方法来创建RDD:

  1. SparkContext的wholeTextFiles()方法,可以针对一个目录中的大量小文件,返回由(fileName,fileContent)组成的pair,即pairRDD,而不是普通的RDD。该方法返回的是文件名字和文件中的具体内容;而普通的textFile()方法返回的RDD中,每个元素就是文本中一行文本。
  2. SparkContext的sequenceFileK,V方法,可以针对SequenceFile创建RDD,K和V泛型类型就是SequenceFile的key和value的类型。K和V要求必须是Hadoop的序列化机制,比如IntWritable、Text等。
  3. SparkContext的hadoopRDD()方法,对于Hadoop的自定义输入类型,可以创建RDD。该方法接收JobConf、InputFormatClass、Key和Value的Class。
  4. SparkContext的hadoopRDD()方法,对于Hadoop的自定义输入类型,可以创建RDD。该方法接收JobConf、InputFormatClass、Key和Value的Class。
创建 DataFrame 文件(自己写的)

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

DataFrame和DataSet

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的小记

DataFrame 中提供了详细的数据结构信息即为 schema (数据集中包含哪些列,列名称,列类型)

structField、structType、schame

  1. 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)//{}
    
  2. 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] {}
    
  3. 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) 
    */

RDD,DataFrame,DataSet的相互转换

ps.参考

Spark RDD、DataFrame、Dataset相互转换

1 基于RDD创建DataFrame (RDD => DataFrame)
一 . 构建 Schema
  1. 构建 RDD[Row]

  2. 构建schema

  3. 调用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()
  }

三. 通过反射获取 Schema

跟自动推断差不多,不过需要创建一个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()
  }
}
2 基于数据源创建 DataFrame
 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("")

  }

3 基于DataSet创建DataFrame
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)
}

1 基于RDD创建DataSet(RDD => DataSet)

​ spark 2.x 之后 Scala API中Dataframe只是DataSet[Row] 类型的别名,在转DataSet不用指定Row类型

一 . createDataSet

代码: 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()


  }
}

二. 自动推断(toDS)自动推断的类型要有所区分
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()
    
  }
}

三.反射获取schema

字段名已经通过反射获取

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)
}

DataFrame <=> DataSet
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)
}

你可能感兴趣的:(关于RDD、DataFrame和Dstream的几个常识(补充 DataSet)的笔记)