上文介绍了spark的各种组件和入门,本文主要介绍spark读入文件以及数据格式(RDD/DataFrame)
1、json文件读取
val df = spark.read.json("E:/people.json")
df.show()//将DataFrame的内容显示到页面
2、CSV文件读取(注意编码要UTF-8)
df=spark.read.csv("E:/emp1.csv")
也可以这样设置
val result = spark.read.format("csv")
.option("delimiter", "|")//分隔符,默认为逗号,
.option("header", "true")//第一行不作为数据内容,作为标题
.option("quote", "'")//quote 引号字符,默认为双引号"
.option("nullValue", "\\N") //指定一个字符串代表 null 值
.option("inferSchema", "true")//header 第一行不作为数据内容,作为标题
.load("test-in/csv/csv_with_header.csv")
完整案例如下所示:
import org.apache.spark.sql.SparkSession
/**
*scala版本的DataFrame的常用操作。
*通常将DataFrame存储在一张临时表中,后面通过执行sql的方式执行操作。
*@authoryangshaojun
*#date2019/4/39:29
*@version1.0
*/
object SparkSql_002_DataFrameNormalOperation{
def main(args:Array[String]):Unit={
val spark=SparkSession
.builder()
.master("local")
.appName("SparkSql_003_DataFrameNormalOperation")
.getOrCreate()
//等同于SQLContext对象sc.read().json("../.json")以及sc.read().load(../.json)
val df=spark.read.csv("E:/emp1.csv")
val result=spark.read.format("csv")
.option("delimiter",",")
.option("inferSchema",true.toString)//这是自动推断属性列的数据类型。
.option("header","true")
.option("quote","'")
.option("nullValue","\\N")
.option("inferSchema","true")
.load("E:/emp1.csv")
result.show()
result.createOrReplaceTempView("result")//创建临时表
spark.sql("SELECT * FROM result where score>60").show()
}
}
3、TXT
A、spark.read.textFile方法(返回是一个整体很难看)
val testtxt=spark.read.textFile("E:/emp1.txt")
testtxt.show()//成功读取文件数据
testtxt.createOrReplaceTempView("testtxt")//创建临时视图,此视图的生命周期与用于创建此数据集的[SparkSession]相关联。
±-----+
| value|
±-----+
| 张绣,98|
|张老三,55|
| 柳丝,87|
| 赵四,56|
|张无忌,99|
|朱元璋,91|
±-----+
2、(优先推荐)spark.sparkContext.textFile
返回的是个RDD,可以通过createDataFrame方法将RDD转为DataFrame进行查询
val spark = SparkSession
.builder()
.appName("testpeople")
.master("local")
.getOrCreate()
// 使用createDataFrame
val FileRDD = spark.sparkContext.textFile("C:/Users/Administrator/Desktop/课程代码/testT.txt",2).map{
lines=>valline=lines.split(",")Row(line(0),line(1).toInt)
}
整体案例如下所示:
package com.cdpsql1
import org.apache.spark.sql.{Row,SparkSession}
import org.apache.spark.sql.types._
import scala.collection.mutable
import java.text.SimpleDateFormat
object SparkSqlExample1{
def main(args:Array[String]):Unit={
val spark=SparkSession
.builder()
.master("local")
.appName("test")
.config("spark.sql.shuffle.partitions","5")
.getOrCreate()
//表结构
val ScoreSchema:StructType=StructType(mutable.ArraySeq(
StructField("name",StringType,nullable=false),
StructField("score",IntegerType,nullable=false)
))
//导入数据
val ScoreData=spark.sparkContext.textFile("E:/emp1.txt").map{
lines=>val line=lines.split(",")
Row(line(0),line(1).toInt)
}
//转表
val ScoreTable=spark.createDataFrame(ScoreData,ScoreSchema)
ScoreTable.createOrReplaceTempView("Score")
spark.sql("select * from Score where score>60").show()
}
}
也可以用case class定义表结构
//定义case class,相当于表结构
case class People(var name:String,var sc:Int)
import org.apache.spark.{SparkContext, SparkConf}
import org.apache.spark.sql.{SparkSession}
object TestDataFrame1 {
def main(args: Array[String]): Unit = {
val spark=SparkSession
.builder()
.master("local")
.appName("test")
.config("spark.sql.shuffle.partitions","5")
.getOrCreate()
val sqlContext=spark.sqlContext
//先将RDD转化成case class 数据类型,然后再通过toDF()方法隐式转换成DataFrame
import sqlContext.implicits._
// 将本地的数据读入 RDD, 并将 RDD 与 case class 关联
val peopleRDD = spark.sparkContext.textFile("E:/emp1.txt")
.map{line => People(line.split(",")(0), line.split(",")(1).trim.toInt)}
// 将RDD 转换成 DataFrames
val df = peopleRDD.toDF()
//将DataFrames创建成一个临时的视图
df.createOrReplaceTempView("people")
//使用SQL语句进行查询
df.show()
}
}
//定义case class,相当于表结构
case class People(var name:String,var sc:Int)
import org.apache.spark.sql.{SparkSession}
object TestDataFrame1 {
def main(args: Array[String]): Unit = {
val spark=SparkSession
.builder()
.master("local")
.appName("test")
.config("spark.sql.shuffle.partitions","5")
.getOrCreate()
//先将RDD转化成case class 数据类型,然后再通过toDF()方法隐式转换成DataFrame
import spark.implicits._
// 将本地的数据读入 RDD, 并将 RDD 与 case class 关联
val peopleRDD = spark.sparkContext.textFile("E:/emp1.txt")
.map{line => People(line.split(",")(0), line.split(",")(1).trim.toInt)}
// 将RDD 转换成 DataFrames
val df = peopleRDD.toDF()
//将DataFrames创建成一个临时的视图
df.createOrReplaceTempView("people")
//使用SQL语句进行查询
df.show()
}
package t1
import org.apache.spark.sql.{SparkSession,Row}
object r4 {
def main(a: Array[String])={
val spark=SparkSession
.builder()
.master("local")
.appName("test")
//.config("spark.sql.shuffle.partitions","1")
.getOrCreate()
import spark.implicits._
val pd=spark.sparkContext.textFile("E:/emp1.txt").map{
lines => val line=lines.split(",")
person(line(0), line(1).toInt)
}
val pe=pd.toDF()
pe.show()
}
}
分列读入,方便后期filter过滤
val pd = spark.sparkContext.textFile("E:/emp1.txt").map {
line => {
val f=line.split(",")
val name=f(0)
val sc=f(1).toInt
(name,sc)
}
}
val x=pd.filter(t => t._2.toInt > 60)
x.foreach(println)
val pd=spark.sparkContext.textFile("E:/emp1.txt").map{
line=>person(line.split(",")(0),line.split(",")(1).toInt)
}.toDF("X1","X2")
pd.show()
val x=pd.filter($"X2">60)
x.show()
接下来说的是spark中的数据格式:RDD与DataFrame
RDD和DataFrame在前文已经介绍很多次了,这里我们详细介绍这两种数据格式
RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。
1.弹性 正常情况下,数据是存放到内存里的,但是如果说内存放不下这么多数据时,这时候就会写到磁盘。RDD的这种自动进行内存和磁盘之间权衡和切换的机制,就是RDD的弹性的特点所在。对用户来说是透明的。
2.分布式
简单说就是数据存放到不同机器上,1个Rdd=多个Partition,Parition就是分区,这个分区其实是个非常重要的概念,为什么这么说呢?Paritition本质上只是一部分数据,它和Task是一一对应的,task在提交的时候又有什么依据呢?其实又跟这个有关系
3.数据集
数据集这个概念也是相当有意思,你以为就是简单的一堆数据吗?答案是否定 ,RDD可以理解为“数据+对数据操作”,RDD的基本单位是partition,以及每个分片的操作函数,也就是算子。当然了,RDD还“记录”着自身的血缘关系,对parent RDD的依赖,官方叫“lineage”。仅仅这么简单吗?这涉及到RDD的容错性,当某个节点挂掉了,这时候数据就丢失了,怎么办?spark程序挂掉了?那这也太low了,RDD是有很强的容错性的,当它发现自己的数据丢失了以后,会自动从自己来源的数据进行重计算,重新获取自己这份数据,这就是“血缘关系”。
由上可知,RDD特点为:分区,不可变,并行操作。
a, 分区
每一个 RDD都被分为多个分区,每个分区 运行在集群的不同节点上。
RDD可以包含PYTHON、JAVA、scala中任意类型的对象,甚至是用户自定义对象。
逻辑上我们可以将 RDD 理解成一个大的数组,数组中的每个元素就代表一个分区 (Partition) 。
在物理存储中,每个分区指向一个存储在内存或者硬盘中的数据块 (Block) ,其实这个数据块就是每个 task 计算出的数据块,它们可以分布在不同的节点上。
所以,RDD 只是抽象意义的数据集合,分区内部并不会存储具体的数据,只会存储它在该 RDD 中的 index,通过该 RDD 的 ID 和分区的 index 可以唯一确定对应数据块的编号,然后通过底层存储层的接口提取到数据进行处理。
在集群中,各个节点上的数据块会尽可能的存储在内存中,只有当内存没有空间时才会放入硬盘存储,这样可以最大化的减少硬盘 IO 的开销。
b,不可变
不可变性是指每个 RDD 都是只读的,它所包含的分区信息是不可变的。由于已有的 RDD 是不可变的,所以我们只有对现有的 RDD 进行转化 (Transformation) 操作,才能得到新的 RDD ,一步一步的计算出我们想要的结果。
这样会带来这样的好处:我们在 RDD 的计算过程中,不需要立刻去存储计算出的数据本身,我们只要记录每个 RDD 是经过哪些转化操作得来的,即:依赖关系,这样一方面可以提高计算效率,一方面是错误恢复会更加容易。如果在计算过程中,第 N 步输出的 RDD 的节点发生故障,数据丢失,那么可以根据依赖关系从第 N-1 步去重新计算出该 RDD,这也是 RDD 叫做"弹性"分布式数据集的一个原因。
c,并行操作
因为 RDD 的分区特性,所以其天然支持并行处理的特性。即不同节点上的数据可以分别被处理,然后生成一个新的 RDD。
RDD的创建方式一般有三种,1、读入文件;2、创建,sc.parallelize(array),3其他形式转化
下文会接着介绍RDD的特性与算子
DataFrame与RDD的区别:
从上面的图中可以看出DataFrame和RDD的区别。RDD是分布式的 Java对象的集合,比如,RDD[Person]是以Person为类型参数,但是,Person类的内部结构对于RDD而言却是不可知的。DataFrame是一种以RDD为基础的分布式数据集,也就是分布式的Row对象的集合(每个Row对象代表一行记录),提供了详细的结构信息,也就是我们经常说的模式(schema),Spark SQL可以清楚地知道该数据集中包含哪些列、每列的名称和类型。
和RDD一样,DataFrame的各种变换操作也采用惰性机制,只是记录了各种转换的逻辑转换路线图(是一个DAG图),不会发生真正的计算,这个DAG图相当于一个逻辑查询计划,最终,会被翻译成物理查询计划,生成RDD DAG,按照之前介绍的RDD DAG的执行方式去完成最终的计算得到结果。
RDD怎么转化为DATAFRAME?
一般来说有两种方法:class case和StructType
参照上文读入文件的方法解决。
综上,SPARK中的DATAFRAME特点如下:
1)分布式的数据集,并且以列的方式组合的。相当于具有schema的RDD
2)相当于关系型数据库中的表,但是底层有优化
3)提供了一些抽象的操作,如select、filter、aggregation、plot(具体api操作在下文中:SPARK基础3(DataFrame操作))
4)它是由于R语言或者Pandas语言处理小数据集的经验应用到处理分布式大数据集上
5)在1.3版本之前,叫SchemaRDD