使用sql来进行操作,简化rdd的开发
DataFrame是一种以rdd为基础的分布式数据集,也就类似于二维表格,只关心数据的含义,提供详细的结构信息
DataSet是分布式数据集合,,是DataFrame的一个扩展
sparkcore中的上下文环境对象是sparkContext,sparksql中的上下文就用的sparksession
新建一个user.json
spark读取的json要求每一行是json格式
打开spark-shell.cmd
val df = spark.read.json("user.json")
df.show //查看数据
df.createTempView("user") //创建临时视图
spark.sql("select * from user where age=18").show
def main(args: Array[String]): Unit = {
//创建sparkSql运行环境
val sparkSql: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSql")
val session = SparkSession.builder().config(sparkSql).getOrCreate()
session.stop()
}
def main(args: Array[String]): Unit = {
//创建sparkSql运行环境
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sql")
// spark 就是上下文环境
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
//读取当前idea工程下的data下的user.json
val df: DataFrame = spark.read.json("data/user.json")
// 创建一个当前环境只能有一个的视图
df.createOrReplaceTempView("user")
// 通过sql进行查询
spark.sql("select name,age+1 from user").show()
// 通过DSL进行查询
//需要使用转化操作的时候需要引入上下文下的implicits
import spark.implicits._
df.select("name","age").show()
df.select('age + 1).show()
// dataSet
// 本质dataframe就是一个dataset[row]
val data = Seq(1,2,3,4)
data.toDS().show()
// rdd <=>DataFrame
val value: RDD[(Int, String)] = spark.sparkContext.makeRDD(List((1, "zhangsan"), (2, "lisi")))
val d: DataFrame = value.toDF("id", "name")
d.rdd
// DataFrame <=> DataSet
val ds: Dataset[User] = d.as[User]
ds.toDF().show()
// rdd <=> DataSet
val ds1: Dataset[User] = value.map {
case (a, b) => {
User(a, b)
}
}.toDS() // 包装满足ds的条件然后直接转换
ds1.rdd // 转换回rdd
spark.close()
}
case class User(id:Int,name:String)
临时表
// 创建视图有可能重复
df.createTempView
// 全局创建有可能重复
df.createGlobalTempView
// 创建视图防止有多个
df.createOrReplaceTempView
// 全局创建防止有多个
df.createOrReplaceGlobalTempView
// 创建全局在查询的时候需要全路径访问要加上global_temp.
// 且不能修改数据只能查询
DataFrame提供一个特定语言区管理结构化数据,可以在scala,java,python和r中使用DSL,使用DSL语法风格就不必创建临时视图了
dataframe和dataset进行很多操作都需要导入这个,是当前上下文的对象spark
import spark.implicits._
DataFrame其实是特定泛型的dataSet
dataframe转换dataset就差一个业务类型class
// 输出结构
df.printSchema
// 查询age列
df.select("age").show
// 查看年龄+1
import session.implicits._ //是当前上下文变量的
df.select($"age"+1).show //需要加上$或者使用'
df.select('age +1).show
// 条件过滤
df.filter('age > 17).show
// rdd转换dataframe
val rdd = sc.makeRDD(List(1,2,3,4))
val df = rdd.toDF("id")
// dataframe转换rdd
val rdd = df.rdd
DataSet是具有强类型的数据集合,需要提供对应的类型信息
// 创建dataset
case class Person(name:String,age:Long)//创建类测试用
val list = List(Person("aa",12),Person("bb",12))
list.toDS //结果:res20: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]
list.toDS.show
// dataframe转换dataset
df// res23: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
case class Emp(age:Long,name:String) //创建指定类型的对象
df.as[Emp] //加上类型:结果=org.apache.spark.sql.Dataset[Emp] = [age: bigint, name: string]
//dataset转换dataframe
df.toDF
//dataset和rdd相互转换
rdd.toDS //rdd转换dataset
ds.rdd //dataset转换rdd
用户自定义函数用在使用上下文.sql对列进行函数操作
val session = SparkSession.builder().config(sparkSql).getOrCreate()
val df: DataFrame = session.read.json("data/user.json")
df.createOrReplaceTempView("user")
session.udf.register("prefixName",(name:String)=>{//进行注册函数
"Name:"+ name
})
session.sql("select age,prefixName(name) from user").show
如果我们需要自定义一个聚合函数的话那么就要用到自定义聚合函数类
弱类型容易把数据搞错
def main(args: Array[String]): Unit = {
//创建sparkSql运行环境
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sql")
// spark 就是上下文环境
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
val df: DataFrame = spark.read.json("data/user.json")
df.createOrReplaceTempView("user")
spark.udf.register("ageAge",new MyAvgUDAF())
spark.sql("select ageAge(age) from user").show()
spark.close()
}
// 自定义聚合函数类
class MyAvgUDAF extends UserDefinedAggregateFunction{
// 输入结构 input 传入数据的结构
override def inputSchema: StructType = {
StructType(
Array(
StructField("age",LongType)
)
)
}
// 缓冲区的数据结构 buffer
override def bufferSchema: StructType = {
StructType(
Array(
StructField("total",LongType),
StructField("count",LongType)
)
)
}
// 函数计算结果的数据类型 out
override def dataType: DataType = LongType
// 函数的稳定性
override def deterministic: Boolean = true
// 缓冲区初始化
override def initialize(buffer: MutableAggregationBuffer): Unit = {
buffer(0) = 0L //初始化缓冲区的值等同于 buffer.update(0,0L)
buffer(1) = 0L
}
// 根据输入的值更新缓冲区数据
override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
buffer.update(0,buffer.getLong(0) + input.getLong(0))
buffer.update(1,buffer.getLong(1)+1)
}
// 缓冲区合并
override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
buffer1.update(0,buffer1.getLong(0) + buffer2.getLong(0))
buffer1.update(1,buffer1.getLong(1) + buffer2.getLong(1))
}
// 计算平均值
override def evaluate(buffer: Row): Any = {
buffer.getLong(0)/buffer.getLong(1)
}
}
版本不对可能会无法使用3.0后才能使用
def main(args: Array[String]): Unit = {
//创建sparkSql运行环境
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sql")
// spark 就是上下文环境
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
val df: DataFrame = spark.read.json("data/user.json")
df.createOrReplaceTempView("user")
spark.udf.register("ageAge",functions.udaf(new MyAvgUDAF()))
spark.sql("select ageAge(age) from user").show()
spark.close()
}
// 自定义聚合函数类
// Aggregator 定义泛型
// in: 输入的数据类型 buf: 缓冲区的类型 out: 输出的数据类型
case class Buff(var total:Long,var count:Long)
class MyAvgUDAF extends Aggregator[Long,Buff,Long]{
// 缓冲区的初始化
override def zero: Buff = {
Buff(0L,0L)
}
// 根据输入的数据更新缓冲区的数据
override def reduce(b: Buff, a: Long): Buff = {
b.total = b.total + a
b.count = b.count + 1
b
}
// 合并缓冲区
override def merge(b1: Buff, b2: Buff): Buff = {
b1.total = b1.total + b2.total
b1.count = b1.count + b2.count
b1
}
// 计算结果
override def finish(reduction: Buff): Long = {
reduction.total / reduction.count
}
// 缓冲区的编码操作
override def bufferEncoder: Encoder[Buff] = Encoders.product
// 输出的编码操作
override def outputEncoder: Encoder[Long] = Encoders.scalaLong
}
早期版本使用方式
def main(args: Array[String]): Unit = {
//创建sparkSql运行环境
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sql")
// spark 就是上下文环境
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
val df: DataFrame = spark.read.json("data/user.json")
import spark.implicits._
val ds: Dataset[User] = df.as[User]
// 将udf函数转换成查询的列对象
val udaf: TypedColumn[User, Long] = new MyAvgUDAF().toColumn
ds.select(udaf).show()
spark.close()
}
// 自定义聚合函数类
// Aggregator 定义泛型类
// in: 输入的数据类型 buf: 缓冲区的类型 out: 输出的数据类型
case class User(name:String,age:Long)
case class Buff(var total:Long,var count:Long)
class MyAvgUDAF extends Aggregator[User,Buff,Long]{
// 缓冲区的初始化
override def zero: Buff = {
Buff(0L,0L)
}
// 根据输入的数据更新缓冲区的数据
override def reduce(b: Buff, a: User): Buff = {
b.total = b.total + a.age
b.count = b.count + 1
b
}
// 合并缓冲区
override def merge(b1: Buff, b2: Buff): Buff = {
b1.total = b1.total + b2.total
b1.count = b1.count + b2.count
b1
}
// 计算结果
override def finish(reduction: Buff): Long = {
reduction.total / reduction.count
}
// 缓冲区的编码操作
override def bufferEncoder: Encoder[Buff] = Encoders.product
// 输出的编码操作
override def outputEncoder: Encoder[Long] = Encoders.scalaLong
}
sparksql提供了通用的保存数据和数据加载的方式,sparksql默认读取和保存的文件格式为parquet
spark.read.load 是加载数据的通用方法,默认只能读取链式存储的数据
spark.read.format("json").load("json路径") // 读取json数据
spark.read.json("json路径") // 直接读取json文件
spark.sql("select * from json.`路径`").show() // sql进行转换直接读取
json
sparksql能够自动 推测json格式,但前提是要满足每一行都满足json的格式
csv
spark.read.format("csv").option("sep", ",").option("inferSchema",true).option("header","true").load("路径")
Mysql
sparksql可以通过jdbc从关系数据库中读取数据的方式创建DataFrame,通过一系列的计算之后,还可以写会关系型数据库中
idea需要的依赖
mysql
mysql-connector-java
5.1.47
def main(args: Array[String]): Unit = {
val mysql: SparkConf = new SparkConf().setMaster("local[*]").setAppName("mysql")
val session: SparkSession = SparkSession.builder().config(mysql).getOrCreate()
//读取mysql数据
val df: DataFrame = session.read
.format("jdbc")
.option("url", "jdbc:mysql://192.168.9.140:3306/contest?")
.option("driver", "com.mysql.jdbc.Driver")
.option("user", "root")
.option("password", "Aa12589+")
.option("dbtable", "user1")
.load()
df.show()
// 写入mysql数据
df.write
.format("jdbc")
.option("url", "jdbc:mysql://192.168.9.140:3306/contest?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false")
.option("driver", "com.mysql.jdbc.Driver")
.option("user", "root")
.option("password", "Aa12589+")
.option("dbtable", "user2")
.mode(SaveMode.Append)
.save()
session.close()
}
df.write.sava("路径") // 保存文件以parquet文件格式
df.write.format("json").sava("路径") // 保存json文件
以上的操作保存如果存在是会报错的
df.write.format("json").mode("append").save("output") // append追加, overwrite覆盖,ignore 忽略
spark中默认有内置的hive
首先我们打开spark-shell
spark目录下没有什么
spark.sql("show tables").show() // 会产生一个源存储目录metastore_db
当我们用json数据创建一个临时视图表
因为我用的是spark on yarn,所以文件需要上传到hdfs
且需要删除虚拟机spark目录下的元数据目录,因为会和hdfs里的冲突
hadoop fs -moveFromLocal ./user.json /data/user.json
var df = spark.read.json("/data/user.json")
df.createOrReplaceTempView("user")
spark.sql("show tables").show()
内置hive创建表插入数据
创建一个表
spark.sql("create table user1(id int)")
创建一个id.txt,一行一个数据
插入到表中
spark.sql("load data local inpath '/bigdata/spark/id.txt' into table user1")
操作本地hive,把hvie的hive-site.xml复制到spark的conf下,还有把mysql的连接驱动包放到spark下的jar
然后重新启动即可
idea操作hive
首先导入依赖
把hive-site.xml复制到resources下
org.apache.spark
spark-hive_2.12
2.4.7
org.apache.hive
hive-exec
2.3.4
def main(args: Array[String]): Unit = {
// 使用spark连接外置hive
val hive: SparkConf = new SparkConf().setMaster("local[*]").setAppName("hive")
//enableHiveSupport 启用hive支持
val session: SparkSession = SparkSession.builder().enableHiveSupport().config(hive).getOrCreate()
//拷贝hive-site.xml到resources下
//启用hive支持
session.close()
}
操作连接hivemysql
sbin/start-thriftserver.sh
spark下的,需要启动hive的server
bin/beeline -u jdbc:hive2://master:10000 -n root