Explicación detallada de SparkSQL

Tabla de contenido

Descripción general de SparkSQL

¿Qué es Spark SQL?

Funciones de Spark SQL

¿Qué es el marco de datos?

La diferencia entre RDD y DataFrame:

¿Qué es el conjunto de datos?

Programación SparkSQL

nuevo punto de partida

Marco de datos

crear

sintaxis SQL

sintaxis DSL

Convertir RDD a DataFrame y DataFrame a RDD

Conjunto de datos

Crear conjunto de datos

Marco de datos a conjunto de datos y conjunto de datos a marco de datos

RDD a conjunto de datos y conjunto de datos a RDD

La relación entre RDD, DataFrame y DataSet

Diferencia de versión

Características comunes entre los tres.

La diferencia entre los tres

IDEA crea el programa SparkSQL

función definida por el usuario

1. Crear marco de datos

2. Función agregada definida por el usuario (tipo débil)

Cargando y guardando datos

Métodos universales de carga y guardado.

parquet

JSON

CSV

mysql

Colmena

Ejemplo de código SparkSQL


Descripción general de SparkSQL

¿Qué es Spark SQL?

Spark SQL es un módulo utilizado por Spark para procesar datos estructurados. Proporciona 2 abstracciones de programación: DataFrame y
DataSet y funciona como un motor de consultas SQL distribuido.

Funciones de Spark SQL

(1), fácil de integrar
Mezcle consultas SQL a la perfección con programas Spark
(2) Método de acceso a datos unificado
Conéctese a cualquier fuente de datos de la misma manera
(3), compatible con colmena
Ejecute consultas SQL o HiveQL en un almacén existente
(4), conexión de datos estándar
Conéctese a través de JDBC u ODBC

¿Qué es el marco de datos?

Al igual que RDD, DataFrame también es un contenedor de datos distribuidos. Sin embargo, DataFrame se parece más a una tabla bidimensional en una base de datos tradicional.
Grid, además de los datos, también registra la información estructural de los datos, es decir, el esquema. Al mismo tiempo, al igual que Hive, DataFrame también admite
Admite tipos de datos anidados (estructura, matriz y mapa). Desde la perspectiva de la facilidad de uso de la API, la API DataFrame proporciona
Un conjunto de operaciones relacionales de alto nivel es más amigable y tiene un umbral más bajo que la API RDD funcional.

La diferencia entre RDD y DataFrame :

ejemplo:
usuarios . unirse ( eventos , usuarios ( "id" ) === eventos ( "uid" )). filtro ( eventos ( "fecha" ) > "2022-01-01" )

¿Qué es el conjunto de datos ?

DataSet es una colección de datos distribuidos. DataSet es una nueva abstracción agregada en Spark1.6 y una extensión de DataFrame.
exhibición. Proporciona las ventajas de los RDD (escritura fuerte, la capacidad de utilizar potentes funciones lambda), así como una ejecución optimizada de SparkSQL.
Las ventajas del motor. Los DataSets también pueden utilizar transformaciones funcionales (mapa operativo, mapa plano, filtro, etc.).
(1), es una extensión de Dataframe API y es la última abstracción de datos de Spark.
(2) Estilo API fácil de usar, con funciones de verificación de seguridad de tipos y optimización de consultas de marco de datos.
(3) El conjunto de datos admite códecs. Cuando necesita acceder a datos que no son del montón, puede evitar deserializar todo el objeto y mejorar la eficiencia.
Tasa.
(4).La clase de muestra se utiliza para definir la información estructural de los datos en el conjunto de datos. El nombre de cada atributo en la clase de muestra se asigna directamente a
El nombre del campo en el conjunto de datos.
(5) El marco de datos es una columna especial del conjunto de datos, Marco de datos = Conjunto de datos [Fila], por lo que puede usar el método as para
El marco de datos se convierte en un conjunto de datos. La fila es un tipo, al igual que Auto y Persona. Toda la información de la estructura de la tabla.
Utilizo Row para representar información. (6), DataSet está fuertemente tipado. Por ejemplo, puede haber Conjunto de datos[Coche], Conjunto de datos[Persona].
(7) DataFrame solo conoce los campos, pero no conoce los tipos de campos, por lo que no se puede utilizar al realizar estas operaciones.
Compruebe si el tipo falla durante la compilación. Por ejemplo, puede realizar una operación de resta en una cadena e informarla solo durante la ejecución.
Es incorrecto, y DataSet no solo conoce los campos, sino que también conoce los tipos de campos, por lo que tiene una verificación de errores más estricta. Al igual que JSON
Analogía entre elefantes y objetos de clase.

Programación SparkSQL

nuevo punto de partida

En la versión anterior, SparkSQL proporciona dos puntos de partida de consultas SQL: uno se llama SQLContext, que utiliza el propio Spark.
Consultas SQL proporcionadas; un HiveContext utilizado para conectarse a consultas de Hive.
SparkSession es el último punto de partida de consultas SQL de Spark y es esencialmente una combinación de SQLContext y HiveContext.
Por lo tanto, las API disponibles en SQLContext y HiveContext también están disponibles en SparkSession.
SparkSession encapsula SparkContext internamente, por lo que SparkContext completa los cálculos.

Marco de datos

La API DataFrame de SparkSQL nos permite usar DataFrames sin tener que registrar tablas temporales o producir SQL
expresión. La API DataFrame tiene operaciones de transformación y operaciones de acción.

crear

En Spark SQL, SparkSession es el punto de entrada para crear DataFrame y ejecutar SQL.
DataFrame viene de tres maneras:
(1) Crear a través de la fuente de datos Spark;
(2) Convertir desde un RDD existente;
(3) También puede consultar y regresar desde Hive Table.
Crear desde fuente de datos Spark
// 1. Ver el formato de archivo creado por la fuente de datos Spark
//chispa.leer.
// 2. Leer el archivo json para crear DataFrame
// Crea un archivo user.json
val df = spark.read.json("/usr/local/soft/data/user.json")
// 3. Mostrar resultados
df.mostrar
df.createTempView("usuario")
spark.sql("seleccionar * del usuario").mostrar
Nota: Si obtiene datos de la memoria, Spark puede saber cuál es el tipo de datos. Si es un número, por defecto
Se trata como Int; sin embargo, no se puede determinar el tipo de número leído del archivo y el índice se recibe utilizando bigint.
Se puede convertir al tipo Long, pero no al tipo Int.
  • Convertir desde un RDD existente (explicado más adelante)
  • También puede consultar y regresar desde Hive Table (se explica más adelante)

sintaxis SQL

El estilo de sintaxis SQL significa que utilizamos declaraciones SQL al consultar datos. Este estilo de consulta debe tener
Vista temporal o vista global para ayudar.
(1), lea el archivo JSON para crear DataFrame
val df = spark.read.json("/usr/local/soft/data/user.json")
(2) Cree una tabla temporal para el DataFrame
df.createOrReplaceTempView("personas")
Nota: La diferencia entre Vista y tabla es que la Vista no se puede modificar y solo se puede consultar, mientras que la tabla se puede agregar, eliminar, modificar y consultar.
(3) Consultar la tabla completa mediante declaraciones SQL
spark.sql("seleccionar * de personas").mostrar
(4), visualización de resultados
df.mostrar
Nota: Las tablas temporales ordinarias están dentro del alcance de la sesión. Si desea que sean válidas en el alcance de la aplicación, puede usar tablas temporales globales.
superficie. Cuando se utilizan tablas temporales globales, se requiere acceso completo a la ruta, como por ejemplo: global_temp.people
spark.newSession.sql("seleccionar * de personas").show 报错
(5) Cree una tabla global para DataFrame
df.createOrReplaceGlobalTempView("personas")
df.createGlobalTempView("personas")
spark.newSession.sql("seleccione * de global_temp.people").mostrar

sintaxis DSL

DataFrame proporciona un lenguaje específico de dominio (DSL) para gestionar datos estructurados. Se puede utilizar en Scala, Java,
DSL se usa en Python y R. El uso del estilo de sintaxis DSL elimina la necesidad de crear vistas temporales.
(1), crea un marco de datos
val df = spark.read.json("/usr/local/soft/data/user.json")
(2) Ver la información del esquema del DataFrame
df.printSchem
(3), vea solo los datos de la columna "edad"
//df. //tecla tab para ver los métodos en df
df.select("edad").show()
(4) Ver los datos de la columna "nombre de usuario" y los datos "edad+1"
df.select("edad" + 1).show() // informe de error
df.select($"edad" + 1).show()
df.select('edad + 1).show()
df.select($"nombre de usuario",$"edad" + 1).show()
df.select($"nombre de usuario",$"edad" + 1 como "nuevaedad").show() // 取别名
Nota: Cuando se trata de operaciones, cada columna debe usar $ o usar una expresión entre comillas: comillas simples + nombre de campo.
(5) Ver datos con "edad" mayor que "30"
df.filter($"edad" > 20).show()
df.filter($"edad" > 30).show()
(6) Agrupe por "edad" para ver la cantidad de elementos de datos
df.groupBy("edad").count.show()

Convertir RDD a DataFrame y DataFrame a RDD

Al desarrollar en IDE, si necesita operar entre RDD y DF o DS, debe introducir la importación.
chispa.implícita._
La chispa aquí no es el nombre del paquete en Scala, sino el nombre de la variable en el objeto sparkSession creado, por lo que debe
El objeto SparkSession debe crearse primero y luego importarse. El objeto Spark aquí no se puede declarar usando var porque solo Scala
Admite la introducción de objetos modificados con val.
Nota: No se requiere importación en Spark-Shell, esto se hace automáticamente.
val rdd = sc.makeRDD(Lista(1,2,3,4))
//rdd. //tecla tab para ver todos los métodos de rdd
val df = rdd.toDF("identificación")
df.mostrar()
//df. // Tecla Tab para ver todos los métodos de df
df.rdd

Conjunto de datos

DataSet es una colección de datos fuertemente tipada y es necesario proporcionar la información del tipo correspondiente.

Crear conjunto de datos _

(1) Cree un conjunto de datos utilizando la secuencia de clases de muestra
//clase de caso Perso(nombre:Cadena,edad:Long)
lista val = Lista(Perso("zhangsan",30),Persona("lisi",20))
val ds = lista.toDS
ds.mostrar

Marco de datos a conjunto de datos y conjunto de datos a marco de datos

//df // Comprobar si df existe
//clase de caso Emp(edad:Long,nombre de usuario:Cadena)
//val ds = df.as[Emp]
//ds.mostrar()
//ds. // Método de visualización de la tecla Tab
//ds.toDF

RDD a conjunto de datos y conjunto de datos a RDD

val rdd = sc.makeRDD(Lista(Emp(30,"zhangsan"),Emp(20,"lisi")))
rdd.toDS
// Requisito previo: debe ser una clase de muestra para preparar el tipo
val rdd = sc.makeRDD(Lista(1,2,3,4))
rdd.toDS
val rdd = sc.makeRDD(Lista(Emp(30,"zhangsan"),Emp(20,"lisi")))
val ds = rdd.toDS
valor rdd1 = ds.rdd

La relación entre RDD , DataFrame y DataSet

En SarpkSQL, Spark nos proporciona dos nuevas abstracciones, a saber, DataFrame y DataSet.

Diferencia de versión

Según la versión:
Chispa1.0 ---->RDD
Spark1.3 ---->Marco de datos
Spark1.6 ---->Conjunto de datos
Si se proporcionan los mismos datos a estas tres estructuras de datos, darán el mismo resultado después del cálculo respectivamente. la diferencia es
Qué tan eficientemente y cómo se ejecutan. En versiones posteriores de Spark, DataSet puede reemplazar gradualmente a RDD y
DataFrame se convierte en la única interfaz API.

Características comunes entre los tres.

1. RDD, DataFrame y Dataset son conjuntos de datos elásticos distribuidos bajo la plataforma Spark y están diseñados para procesar datos extremadamente grandes.
Se proporciona comodidad.
2. Los tres tienen un mecanismo perezoso: al crear y convertir, como el método del mapa, no se ejecutarán inmediatamente, solo se ejecutarán cuando se encuentren
Cuando la acción es como foreach, los tres comenzarán la operación transversal.
3. Los tres almacenarán en caché automáticamente las operaciones de acuerdo con la memoria de Spark, por lo que incluso si la cantidad de datos es grande, no hay necesidad de preocuparse por quedarse sin memoria.
Desbordamiento.
4. Los tres tienen el concepto de partición.
5. Los tres tienen muchas funciones comunes, como filtrar, clasificar, etc.
6. Muchas operaciones en DataFrame y Dataset requieren soporte de este paquete: importar
chispa.implicits._。
7. Tanto DataFrame como Dataset pueden utilizar la coincidencia de patrones para obtener el valor y el tipo de cada campo.

La diferencia entre los tres

1.RDD:
(1) RDD se usa generalmente junto con Spark Mlib
(2) RDD no admite operaciones sparksql
2.Marco de datos:
(1) A diferencia de RDD y Dataset, el tipo de cada fila de DataFrame se fija en Fila y el valor de cada columna no puede ser directamente
Acceso, el valor de cada campo solo se puede obtener mediante análisis.
(2) DataFrame y Dataset generalmente no se usan junto con Spark Mlib.
(3), DataFrame y Dataset admiten operaciones de SparkSQL, como seleccionar, agrupar, etc., y también pueden notar
Registre la tabla/ventana temporal y realice operaciones de declaración SQL.
(4) DataFrame y Dataset admiten algunos métodos de guardado particularmente convenientes, como guardar en csv, que se puede incorporar con tablas.
encabezado para que los nombres de los campos de cada columna sean claros de un vistazo.
3.Conjunto de datos:
(1), Dataset y DataFrame tienen exactamente las mismas funciones miembro, la única diferencia es el tipo de datos de cada fila.
(2) DataFrame también se puede llamar Dataset[Row]. El tipo de cada fila es Row. No se analiza. ¿Qué tiene exactamente cada fila?
No hay forma de saber los tipos de campos y los tipos de cada campo, solo puede usar el método getAS mencionado anteriormente o el séptimo punto en común.
El patrón mencionado coincide para resaltar campos específicos. En Dataset, el tipo de cada fila no es necesariamente seguro.
Después de agregar la clase de caso, puede obtener libremente la información de cada fila.

IDEA crea el programa SparkSQL

En el desarrollo real, IDEA se utiliza para el desarrollo. Los métodos de empaquetado y ejecución de programas en IDEA son los mismos que los de SparkCore
De manera similar, es necesario agregar nuevas dependencias a las dependencias de Maven:
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-sql -->
<dependencia>
<groupId> org.apache.spark </groupId>
<artifactId> spark-sql_2.12 </artifactId>
<versión > 2.4.5 </versión>
</dependencia>
package com.shujia.core.sql
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
object Demo01_SparkSql {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSQL的运行环境
val sparkConf: SparkConf = new SparkConf()
.setMaster("local[*]")
.setAppName("SparkSql")
// new SparkSession() //表示没有构造器在这可以访问
//原因:构造方法私有化,所以在外面不能直接访问
val spark: SparkSession =
SparkSession.builder().config(sparkConf).getOrCreate()
//builder()构建器构建对象,使用getOrCreate()构建,获取或创建//注意:创建环境的时候需要告知连的是什么,通过config()将配置对象传进去
//在使用DataFrame时,如果设计到转换操作,需要引入转换规则
import spark.implicits._
//TODO 执行逻辑操作
//DataFrame
// val df: DataFrame = spark
// .read
// .json("data/user.json")
// df.show()
//DataFrame =>SQL
// df.createOrReplaceTempView("user")
//
// spark.sql(
// """
// |select * from user
// |""".stripMargin)
// .show()
//
// spark.sql(
// """
// |select age from user
// |""".stripMargin)
// .show()
//
// spark.sql(
// """
// |select avg(age) from user
// |""".stripMargin)
// .show()
//DataFrame =>DSL
//在使用DataFrame时,如果设计到转换操作,需要引入转换规则
// import spark.implicits._
// df.select("age","username").show
// df.select($"age"+ 1 ).show
// df.select('age + 1).show
//TODO DataSet
//DataFrame其实就是特定泛型到DataSet
// val seq: Seq[Int] = Seq(1, 2, 3, 4)
// val ds: Dataset[Int] = seq.toDS()
// ds.show()
//RDD <=> DataFrame
val rdd: RDD[(Int, String, Int)] =
spark.sparkContext.makeRDD(List((1,"zhangsan",20),(2,"lisi",21),
(1,"wangwu",23)))
val df: DataFrame = rdd.toDF("id","name","age")
df.show()
val rdd1: RDD[Row] = df.rddrdd1.foreach(println)
//DataFrame <=> DataSet
val ds: Dataset[User] = df.as[User]
ds.show()
val df1: DataFrame = ds.toDF("id","name","age")
df1.show()
//RDD <=> DataSet
//DataSet:有数据有类型有结构
val ds2: Dataset[User] = rdd.map {
case (id, name, age) => {
User(id, name, age)
}
}.toDS()
ds2.show()
val rdd2: RDD[User] = ds2.rdd
rdd2.foreach(println)
//TODO 关闭环境
spark.close()
}
case class User (id:Int,name:String,age:Int)
}

función definida por el usuario

Los usuarios pueden agregar funciones personalizadas a través de la función spark.udf para implementar funciones personalizadas.
UDF

1. Crear marco de datos

package com.shujia.core.sql
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
object Demo02_SparkSql_UDF {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSQL的运行环境
val sparkConf: SparkConf = new SparkConf()
.setMaster("local[*]")
.setAppName("SparkSql")
val spark: SparkSession =
SparkSession.builder().config(sparkConf).getOrCreate()
import spark.implicits._
//TODO 执行逻辑操作
val df: DataFrame = spark.read.json("data/user.json")
df.createOrReplaceTempView("user")// spark.sql(
// """
// |select age,"name" + username from user
// |""".stripMargin)
// .show()
spark.udf.register("prefixName",(name:String) =>{
"name:"+ name
})
spark.sql(
"""
|select age,prefixName(username) from user
|""".stripMargin)
.show()
//TODO 关闭环境
spark.close()
}
}
Tanto el DataSet fuertemente tipado como el DataFrame débilmente tipado proporcionan funciones agregadas relacionadas, como count(),
countDistinct(),avg(),max(),min(). Además, los usuarios pueden configurar sus propias funciones agregadas personalizadas. Siguendolo
Hereda UserDefinedAggregateFunction para implementar funciones agregadas de tipo débil definidas por el usuario. De la versión Spark3.0
Ya no se recomienda UserDefinedAggregateFunction. Las funciones agregadas fuertemente tipadas se pueden usar de manera uniforme.
Agregador。

2. Función agregada definida por el usuario (tipo débil)

package com.shujia.core.sql
import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.{MutableAggregationBuffer,
UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, LongType, StructField,
StructType}
import org.apache.spark.sql.{DataFrame, Row, SparkSession}
object Demo03_SparkSql_UDAF {
def main(args: Array[String]): Unit = {//TODO 创建SparkSQL的运行环境
val sparkConf: SparkConf = new SparkConf()
.setMaster("local[*]")
.setAppName("SparkSql")
val spark: SparkSession =
SparkSession.builder().config(sparkConf).getOrCreate()
//TODO 执行逻辑操作
val df: DataFrame = spark.read.json("data/user.json")
df.createOrReplaceTempView("user")
spark.udf.register("ageAvg",new MyAvgUDAF)
spark.sql(
"""
|select ageAvg(age) from user
|""".stripMargin)
.show()
//TODO 关闭环境
spark.close()
}
/**
* 自定义聚合函数类:计算年龄的平均值
* 1、继承UserDefinedAggregateFunction
* 2、重写方法(8)
*
*/
class MyAvgUDAF extends UserDefinedAggregateFunction{
//输入数据的结构 IN
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.update(0,0L)
buffer.update(1,0L)
}
//根据输入的值更新缓冲区数据
override def update(buffer: MutableAggregationBuffer, input: Row):
Unit = {
//buffer.getLong(0)缓冲区之前的值 + input.getLong(0)输入的值
buffer.update(0,buffer.getLong(0) + input.getLong(0))
buffer.update(1,buffer.getLong(1) + 1)
}
//缓冲区数据合并
override def merge(buffer1: MutableAggregationBuffer, buffer2:
Row): Unit = {
//x y
//1 2 3 4
//x+y===>x
//x y
//3 3 4
//x+y===>x
//x y
//6 4
//x+y===>x
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)
}
}
}
(用户自定义聚合函数(Spark3.0.0以下版本的强类型)
package com.shujia.core.sql
import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.{Aggregator,
MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, LongType, StructField,
StructType}
import org.apache.spark.sql.{DataFrame, Dataset, Encoder, Encoders,
Row, SparkSession, TypedColumn}
object Demo03_SparkSql_UDAF2 {def main(args: Array[String]): Unit = {
//TODO 创建SparkSQL的运行环境
val sparkConf: SparkConf = new SparkConf()
.setMaster("local[*]")
.setAppName("SparkSql")
val spark: SparkSession =
SparkSession.builder().config(sparkConf).getOrCreate()
import spark.implicits._
//TODO 执行逻辑操作
val df: DataFrame = spark.read.json("data/user.json")
//在Spark3.0.0以下的版本,也就是我们所用的版本是不能在sql中使用强类型UDAF操作
//SQL & DSL
//Spark3.0.0以下的版本,强类型UDAF聚合函数使用DSL语法操作
val ds: Dataset[User] = df.as[User]
//将UDAF函数转为查询的列对象
val udaf: TypedColumn[User, Long] = new MyAvgUDAF().toColumn
//查询
ds.select(udaf).show()
//TODO 关闭环境
spark.close()
}
/**
* 自定义聚合函数类:计算年龄的平均值
* 1、继承UserDefinedAggregateFunction
* 2、重写方法(8)
*
*/
case class User(username:String,age:BigInt)
/**
* 自定义聚合函数类:计算年龄的平均值
* 1、继承 org.apache.spark.sql.expressions.Aggregator,定义泛型
* IN:输入的数据类型 User
* BUF:缓冲区的数据类型
* OUT:输出的数据类型 Long
* 2、重写方法(6)
*
*/
case class Buff(var total:Long,var count:Long)
class MyAvgUDAF extends Aggregator[User,Buff,Long]{
// z & zero都为初始值或零值
//缓冲区的初始化
override def zero: Buff = {
Buff(0L,0L)
}
//根据输入的数据更新缓冲区数据override def reduce(buff: Buff, in: User): Buff = {
buff.total = buff.total + in.age.toInt
buff.count = buff.count + 1
buff
}
//合并缓冲区
override def merge(buff1: Buff, buff2: Buff): Buff = {
buff1.total = buff1.total + buff2.total
buff1.count = buff1.count + buff2.count
buff1
}
//计算结果
override def finish(buff: Buff): Long = {
buff.total / buff.count
}
//缓冲区的编码操作
override def bufferEncoder: Encoder[Buff] = Encoders.product
//输出的编码操作
override def outputEncoder: Encoder[Long] = Encoders.scalaLong
}
}
Función agregada definida por el usuario (tipo fuerte en spark3.0.0 o superior)
Nota: Spark3.0.0 no tienefunctions.udaf . Si desea usarlo, debe modificar la versión de Spark en el archivo pom .
package com.shujia.core.sql
import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.Aggregator
import org.apache.spark.sql.{DataFrame, Encoder, Encoders,
SparkSession, functions}
object Demo04_SparkSql_UDAF1 {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSQL的运行环境
val sparkConf: SparkConf = new SparkConf()
.setMaster("local[*]")
.setAppName("SparkSql")
val spark: SparkSession =
SparkSession.builder().config(sparkConf).getOrCreate()
import spark.implicits._
//TODO 执行逻辑操作
val df: DataFrame = spark.read.json("data/user.json")
df.createOrReplaceTempView("user")spark.udf.register("ageAvg", functions.udaf(new MyAvgUDAF))
spark.sql(
"""
|select ageAvg(age) from user
|""".stripMargin)
.show()
//TODO 关闭环境
spark.close()
}
/**
* 自定义聚合函数类:计算年龄的平均值
* 1、继承 org.apache.spark.sql.expressions.Aggregator,定义泛型
* IN:输入的数据类型 Long
* BUF:缓冲区的数据类型
* OUT:输出的数据类型 Long
* 2、重写方法(6)
*
*/
case class Buff(var total:Long,var count:Long)
class MyAvgUDAF extends Aggregator[Long,Buff,Long]{
// z & zero都为初始值或零值
//缓冲区的初始化
override def zero: Buff = {
Buff(0L,0L)
}
//根据输入的数据更新缓冲区数据
override def reduce(buff: Buff, in: Long): Buff = {
buff.total = buff.total + in
buff.count = buff.count + 1
buff
}
//合并缓冲区
override def merge(buff1: Buff, buff2: Buff): Buff = {
buff1.total = buff1.total + buff2.total
buff1.count = buff1.count + buff2.count
buff1
}
//计算结果
override def finish(buff: Buff): Long = {
buff.total / buff.count
}
//缓冲区的编码操作
override def bufferEncoder: Encoder[Buff] = Encoders.product//输出的编码操作
override def outputEncoder: Encoder[Long] = Encoders.scalaLong
}
}

Cargando y guardando datos

Métodos universales de carga y guardado.

SparkSQL proporciona una forma común de guardar y cargar datos. Común aquí se refiere al uso de la misma API, raíz
Lea y guarde datos en diferentes formatos según diferentes parámetros. El formato de archivo predeterminado leído y guardado por SparkSQL es:
parquet.
(1)Cargar datos
spark.read.load es un método general para cargar datos
spark .read . // método de visualización de la tecla tabulador
Si lee datos en diferentes formatos, puede configurar diferentes formatos de datos.
chispa . leer . formato ( "..." )[. opción ( "..." )]. cargar ( "..." )
1. formato ("..."): especifique el tipo de datos que se cargarán, incluidos "csv", "jdbc", "json", "orc", "parquet" y "textFile".
2. cargar("..."): En los formatos "csv", "jdbc", "json", "orc", "parquet" y "textFile", debe pasar la ruta para cargar los datos.
3. opción ("..."): en el formato "jdbc", debe pasar los parámetros JDBC correspondientes, URL, usuario, contraseña y dbtable. Usamos la API de lectura para cargar el archivo en el DataFrame primero y luego consúltalo, de hecho, también puedes consultar directamente en el archivo:
// Formato de archivo.`Ruta del archivo` _
// Sube user.json a HDFS _
//hadoop fs -poner usuario.json /datos
chispa . sql ( "seleccione * de json.`data/user.json`" ). espectáculo
(2) Guardar datos
df.write.save es un método general para guardar datos
df .write . // Método de visualización de la tecla Tab
df . escribir . formato ( "json" ). guardar ( "/datos/salida1" )
Si guarda datos en diferentes formatos, puede configurar diferentes formatos de datos.
df . escribir . formato ( "..." )[. opción ( "..." )]. guardar ( "..." )
1. formato ("..."): especifica el tipo de datos que se cargarán, incluido
Por ejemplo: "csv", "jdbc", "json", "orc", "parquet" y "textFile".
2. cargar("..."): debe pasarse en formatos "csv", "jdbc", "json", "orc", "parquet" y "textFile".
Ingrese la ruta para cargar datos.
3. opción ("..."): en el formato "jdbc", debe pasar los parámetros JDBC correspondientes, URL, usuario, contraseña y
La operación de guardar de dbtable puede usar SaveMode para especificar cómo procesar los datos. Use el método mode() para configurar
Colocar.
Nota: Estos SaveMode no están bloqueados y no son operaciones atómicas.
SaveMode es una clase de enumeración cuyas constantes incluyen:
df . escribir . modo ( "añadir" ). json ( "datos/salida1" )
df . escribir . modo ( "sobrescribir" ). json ( "datos/salida1" )
df . escribir . modo ( "ignorar" ). json ( "datos/salida1" )

parquet

La fuente de datos predeterminada de SparkSQL es el formato parquet. parquet es un almacenamiento en columnas que puede almacenar datos anidados de manera eficiente
Formato.
Cuando la fuente de datos es un archivo parquet, SparkSQL puede realizar fácilmente todas las operaciones sin utilizar formato. Revisar
El elemento de configuración spark.sql.sources.default puede modificar el formato de fuente de datos predeterminado.
(1)Cargar datos
// Sube archivos a HDFS
//hadoop fs -poner usuarios.parquet /datos
val df = chispa . leer . cargar ( "/datos/usuarios.parquet" )
df . espectáculo
(2)Guardar datos
val df = chispa . leer . json ( "datos/usuario.json" )
df . escribir . modo ( "añadir" ). guardar ( "/datos/salida2" )

JSON

SparkSQL puede inferir automáticamente la estructura de un conjunto de datos JSON y cargarlo como un conjunto de datos [ROW]. capaz de pasar
SparkSession.read.json() para cargar archivos JSON.
Nota: El archivo JSON leído por Spark no es un archivo JSON tradicional. Cada línea debe ser una cadena JSON. El formato es como
Abajo:
{ "nombre" : "zhangsan" }
{ "nombre" : "lisi" , "edad" : 21 }
{ "nombre" : "wangwu" , "edad" : 24 }, { "nombre" : "zhaoliu" , "edad" : 23 }
(1)Importar conversión implícita
importar chispa . implícitos . _
(2) Cargar archivo JSON
ruta de valor = "datos/usuario.json"
val usuarioDF = chispa . leer . json ( ruta )
(3) Crear una tabla temporal
usuarioDF . createOrReplaceTempView ( "usuario" )
(4) Consulta de datos
chispa . sql ( "seleccionar * del usuario" ). espectáculo

CSV

SparkSQL puede configurar la información de la lista del archivo CSV, leer el archivo CSV y establecer la primera línea del archivo CSV como columna de datos .
// Sube archivos a HDFS
//hadoop fs -poner personas.csv /datos
valor df_csv =
chispa . leer . formato ( "csv" ). opción ( "seq" , ";" ). opción ( "inferSchema" , "verdadero" )
. opción ( "encabezado" , "verdadero" ). cargar ( "/datos/personas.csv" )

mysql

SparkSQL puede crear un DataFrame leyendo datos de una base de datos relacional a través de JDBC.
Después de una serie de cálculos en el DataFrame, los datos se pueden volver a escribir en la base de datos relacional. Si se utiliza la operación de chispa
Para hacer esto, puede especificar la ruta del controlador de la base de datos relevante al iniciar el shell o colocar el controlador de la base de datos relevante en la clase Spark.
bajo el camino .
contenedor / chispa - cáscara
--jars mysql - conector - java - 5.1 . 27 contenedores . _ frasco
Operamos Mysql a través de JDBC en IDEA.
(1) Dependencias de importación
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependencia>
<grupoId> mysql </grupoId>
<artifactId> conector-mysql-java </artifactId>
<versión> 5.1.49 </versión>
</dependencia>
(2)Leer datos
package com.shujia.core.sql
import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.Aggregator
import org.apache.spark.sql._
object Demo05_SparkSql_JDBC {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSQL的运行环境
val sparkConf: SparkConf = new SparkConf()
.setMaster("local[*]")
.setAppName("SparkSql")
val spark: SparkSession =
SparkSession.builder().config(sparkConf).getOrCreate()
//TODO 执行逻辑操作
//读取mysql数据
val df: DataFrame = spark.read
.format("jdbc")
.option("url", "jdbc:mysql://master:3306/dtt_data")
//注意mysql5.7用:com.mysql.jdbc.Driver
.option("driver", "com.mysql.jdbc.Driver")
.option("user", "root")
.option("password", "123456")
.option("dbtable", "student")
.load()
// df.show()
df.write
.format("jdbc")
.option("url","jdbc:mysql://master:3306/dtt_data")
.option("driver","com.mysql.jdbc.Driver")
.option("user","root")
.option("password","123456")
.option("dbtable","student_1")
.mode(SaveMode.Append)
.save()
//TODO 关闭环境
spark.close()
}
}

Colmena

ApacheHive es el motor SQL de Hadoop. SparkSQL se puede compilar con o sin soporte de Hive.
SparkSQL, incluido el soporte de Hive, puede admitir acceso a Hive, UDF (función definida por el usuario) y lenguaje de consulta de Hive.
(HiveQL/HQL), etc. Una cosa que debe enfatizarse es que si desea incluir la biblioteca Hive en Spark SQL, no necesita hacer nada.
Instale Hive primero. En términos generales, es mejor introducir la compatibilidad con Hive al compilar Spark SQL para que pueda utilizar este
algunas características. Si descargó la versión binaria de Spark, debería haberse compilado con soporte agregado para Hive.
(1) Colmena incorporada
// Colmena incorporada
//spark-shell
// Primero elimina metastore_db en la carpeta local de spark
// ejecutar de nuevo
chispa . sql ( "mostrar tablas" ). espectáculo
// Verifica la carpeta local de Spark nuevamente
val df = chispa . leer . json ( "datos/usuario.json" )
df . createOrReplaceTempView ( "usuario" )
// Puedes ver que hay una tabla temporal
chispa . sql ( "mostrar tablas" ). espectáculo
// Crea una tabla shujia
chispa . sql ( "crear tabla shujia(id int)" )
// cargar datos
// Antes de cargar datos, debe crear un archivo id.txt en la carpeta de datos.
chispa . sql ( "cargar datos locales inpath'data/id.txt' en la tabla shujia" )
// consulta
chispa . sql ( "seleccionar * de shujia" ). espectáculo
En el desarrollo real, casi nadie utiliza el Hive integrado.
(2) Colmena externa
Si desea conectarse a Hive que se ha implementado externamente, debe seguir los siguientes pasos:
(1) Para hacerse cargo de Hive, Spark necesita copiar el archivo hive-site.xml al directorio conf/;
[root@master conf] # cp hive-site.xml /usr/local/soft/spark-local/conf/
(2) Copie el controlador mysql al directorio jars/;
[paquetes root@master] # cp mysql-connector-java-5.1.49.jar /usr/local/soft/spark-local/jars/
(3) Si no puede acceder a hdfs, debe copiar core-site.xml y hdfs-site.xml al directorio conf/.
(4) Reiniciar Spark-Shell
contenedor / chispa - cáscara
(5) Operación de código Hive
Agregar dependencias:
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-hive -->
<dependencia>
<groupId> org.apache.spark </groupId>
<artifactId> spark-hive_2.12 </artifactId>
<versión > 2.4.5 </versión>
</dependencia>
<!-- https://mvnrepository.com/artifact/org.apache.hive/hive-exec -->
<dependencia>
<groupId> org.apache.hive </groupId>
<artifactId> hive-exec </artifactId>
<versión> 1.2.1 </versión>
</dependencia>

Escribir código:

package com.shujia.core.sql
import org.apache.spark.SparkConf
import org.apache.spark.sql._
object Demo06_SparkSql_Hive {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSQL的运行环境
val sparkConf: SparkConf = new SparkConf()
.setMaster("local[*]")
.setAppName("SparkSql")
val spark: SparkSession =
SparkSession.builder().enableHiveSupport().config(sparkConf).getOrCreat
e()
//使用SparkSQL连接外置的Hive
//1、拷贝hive-site.xml文件到classpath下
//2、启用hive到支持.enableHiveSupport()
//3、增加对应到依赖关系(包含Mysql驱动)
//TODO 执行逻辑操作
spark.sql("show tables").show()
//TODO 关闭环境
spark.close()
}
}

La siguiente es una solución a un determinado informe de error.

//Error al crear una instancia
'org.apache.spark.sql.hive.HiveSessionState':
java.lang.RuntimeException: el directorio temporal raíz: /tmp/hive en HDFS
debe poder escribirse. Los permisos actuales son: ---------
% HADOOP_HOME % \bin\winutils . exe ls E : \tmp\hive
% HADOOP_HOME % \bin\winutils . exe chmod 777 E : \tmp\hive
% HADOOP_HOME % \bin\winutils . exe ls E : \tmp\hive
// Error encontrado: modifique la codificación del juego de caracteres de la base de datos de metadatos de Hive
mysql > alterar el conjunto de caracteres de la colmena de la base de datos latin1 ;

Ejemplo de código SparkSQL

 
//使用spark加载数据
package com.shujia.spark.test
import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}
object DemoSpark_hive {
def main(args: Array[String]): Unit = {
System.setProperty("HADOOP_USER_NAME","root")
//TODO 创建SparkSQL的运行环境
val sparkConf: SparkConf = new SparkConf()
.setMaster("local[*]")
.setAppName("SparkSql")
val spark: SparkSession =
SparkSession.builder().enableHiveSupport().config(sparkConf).getOrCreate()
spark.sql(
"""
|create table user_log_action(
| `date` string,
| `user_id` bigint,
| `session_id` string,
| `page_id` string,
| `action_time` string,
| `search_keyword` string,
| `click_category_id` bigint,
| `click_product_id` bigint,
| `order_category_ids` string,
| `order_product_ids` string,
| `pay_category_ids` string,
| `pay_product_ids` string,
| `city_id` bigint
|)
|row format delimited fields terminated by '\t'
|""".stripMargin)
spark.sql(
"""
|load data local inpath 'data/user_log_action.txt' into table
user_log_action
|""".stripMargin)
spark.sql(
"""
|create table product_info(
| `product_id` bigint,
| `product_name` string,
| `extend_info` string
|)
|row format delimited fields terminated by '\t'
|""".stripMargin)
spark.sql(
"""
|load data local inpath 'data/product_info.txt' into table
product_info
|""".stripMargin)
spark.sql(
"""
|create table city_info(
| `city_id` bigint,
| `city_name` string,
| `area` string
|)
|row format delimited fields terminated by '\t'
|""".stripMargin)
spark.sql(
"""
|load data local inpath 'data/city_info.txt' into table city_info
|""".stripMargin)
spark.sql(
"""
|select * from product_info
|""".stripMargin)
.show()
spark.close()
}
}

Supongo que te gusta

Origin blog.csdn.net/qq_40322236/article/details/128903588
Recomendado
Clasificación