SparkSQLの詳細説明

目次

SparkSQL の概要

Spark SQLとは

Spark SQL の機能

データフレームとは

RDD と DataFrame の違い:

データセットとは

SparkSQL プログラミング

新たな出発点

データフレーム

作成する

SQL 構文

DSL 構文

RDD をデータフレームに変換&データフレームを RDD に変換

データセット

データセットの作成

DataFrame から DataSet、DataSet から DataFrame

RDD から DataSet へ&DataSet から RDD へ

RDD、DataFrame、DataSetの関係

バージョンの違い

3つの共通点

3つの違い

IDEA が SparkSQL プログラムを作成する

ユーザー定義関数

1. データフレームの作成

2. ユーザ定義の集計関数(弱い型)

データのロードと保存

ユニバーサルなロードおよび保存方法

寄木細工の床

JSON

CSV

MySQL

ハイブ

SparkSQL コードサンプル


SparkSQLの概要

Spark SQLとは

Spark SQL は、構造化データを処理するために Spark によって使用されるモジュールであり、DataFrame と DataFrame という 2 つのプログラミング抽象化を提供します。
DataSet であり、分散 SQL クエリ エンジンとして機能します。

Spark SQL の機能

(1)、統合が簡単
SQL クエリと Spark プログラムをシームレスに組み合わせる
(2) 統一されたデータアクセス方式
同じ方法でどのデータ ソースにも接続できます
(3)、Hive と互換性あり
既存のウェアハウスで SQL または HiveQL クエリを実行する
(4)、標準データ接続
JDBC または ODBC 経由で接続する

データフレームとは

RDD と同様に、DataFrame も分散データ コンテナーです。ただし、DataFrame は従来のデータベースの 2 次元テーブルに似ています。
グリッドにはデータに加えて、データの構造情報、つまりスキーマも記録されます。同時に、Hive と同様に、DataFrame もサポートされます。
ネストされたデータ型 (構造体、配列、マップ) をサポートします。API の使いやすさの観点から、DataFrame API は次のことを提供します。
一連の高レベルのリレーショナル操作は、機能的な RDD API よりも使いやすく、しきい値が低くなります。

RDDDataFrameの違い:

例:
ユーザー join ( events , users ( "id" ) === events ( "uid" ))。 フィルター ( イベント ( "日付" ) > "2022-01-01" )

データセットとは

DataSet は分散データ コレクションです。DataSet は、Spark1.6 で追加された新しい抽象化であり、DataFrame の拡張機能です。
展示。RDD の利点 (強力な型指定、強力なラムダ関数を使用する機能) と、SparkSQL の最適化された実行を提供します。
エンジンの利点。DataSet は関数変換 (操作マップ、フラットマップ、フィルターなど) を使用することもできます。
(1) は Dataframe API の拡張機能であり、Spark の最新のデータ抽象化です。
(2) タイプ セーフティ チェックとデータフレーム クエリ最適化機能の両方を備えた、ユーザー フレンドリーな API スタイル。
(3) データセットはコーデックをサポートしており、非ヒープ データにアクセスする必要がある場合、オブジェクト全体の逆シリアル化を回避し、効率を向上させることができます。
レート。
(4). サンプル クラスは、データセット内のデータの構造情報を定義するために使用され、サンプル クラス内の各属性の名前は、直接マッピングされます。
DataSet 内のフィールドの名前。
(5) Dataframe は Dataset の特別な列、DataFrame=Dataset[Row] であるため、as メソッドを使用して
データフレームはデータセットに変換されます。Row は Car や Person と同様に型であり、すべてのテーブル構造情報
情報を表すために Row を使用します。(6)、DataSet は厳密に型指定されています。たとえば、データセット[車]、データセット[人]などがあります。
(7) DataFrame はフィールドのみを認識し、フィールドの型を認識しないため、これらの操作を実行する場合には使用できません。
コンパイル中に型が失敗するかどうかを確認します。たとえば、文字列に対して減算演算を実行し、実行中にのみレポートすることができます。
それは間違いです。DataSet はフィールドを認識しているだけでなく、フィールドのタイプも認識しているため、より厳密なエラー チェックが行われます。JSONと同じように
象とクラスオブジェクトの類似。

SparkSQLプログラミング

新たな出発点

古いバージョンでは、SparkSQL には 2 つの SQL クエリ開始点が用意されています。1 つは SQLContext と呼ばれるもので、Spark 自体によって使用されます。
提供された SQL クエリ。Hive クエリへの接続に使用される HiveContext。
SparkSession は Spark の最新の SQL クエリの開始点であり、本質的には SQLContext と HiveContext の組み合わせです。
したがって、SQLContext および HiveContext で使用できる API は、SparkSession でも使用できます。
SparkSession は内部でsparkContextをカプセル化するため、計算は実際にはsparkContextによって完了します。

データフレーム

SparkSQL の DataFrame API を使用すると、一時テーブルを登録したり SQL を生成したりせずに DataFrame を使用できます。
表現。DataFrame API には、変換操作とアクション操作の両方があります。

作成する

Spark SQL では、SparkSession は DataFrame を作成して SQL を実行するためのエントリ ポイントです。
DataFrame は次の 3 つの方法で提供されます。
(1) Spark データ ソースを通じて作成します。
(2) 既存の RDD から変換します。
(3). Hive テーブルからクエリを実行して返すこともできます。
Spark データソースから作成
//1 . Sparkデータ ソース によって作成されたファイル形式を表示する
//スパーク.読み取り。
//2 . jsonファイルを読み取って DataFrame を作成する
// user.jsonファイル を作成します
val df = spar.read.json("/usr/local/soft/data/user.json")
//3 . 結果の表示
df.ショー
df.createTempView("ユーザー")
smile.sql("ユーザーから * を選択").show
注: メモリからデータを取得する場合、Spark はデータ型を認識できます。数値の場合はデフォルト
Int として扱われますが、ファイルから読み取られた数値の型は判断できないため、bigint を使用してインデックスを受け取ります。
Long型への変換は可能ですが、Int型への変換はできません。
  • 既存の RDD からの変換 (後述)
  • Hive テーブルからクエリを実行して返すこともできます (後で説明します)。

SQL構文

SQL 構文スタイルは、データをクエリするときに SQL ステートメントを使用することを意味します。このスタイルのクエリには、
一時的なビューまたはグローバル ビューを支援します。
(1)、JSONファイルを読み込みDataFrameを作成します
val df = spar.read.json("/usr/local/soft/data/user.json")
(2) DataFrameの一時テーブルを作成する
df.createOrReplaceTempView("人")
注: ビューとテーブルの違いは、ビューは変更できず、クエリのみが可能であるのに対し、テーブルは追加、削除、変更、クエリができることです。
(3) SQL ステートメントによるテーブル全体のクエリ
smile.sql("人から * を選択").show
(4)、結果表示
df.ショー
注: 通常の一時テーブルはセッション スコープ内にありますが、アプリケーション スコープ内で有効にしたい場合は、グローバル テンポラリ テーブルを使用できます。
表面。グローバル一時テーブルを使用する場合は、global_temp.people などのフルパス アクセスが必要です。
smile.newSession.sql("人から * を選択").show 报错
(5) DataFrame グローバルテーブルを作成する
df.createOrReplaceGlobalTempView("人")
df.createGlobalTempView("人")
smile.newSession.sql("select * from global_temp.people").show

DSL構文

DataFrame は、構造化データを管理するためのドメイン固有言語 (DSL) を提供します。Scala、Java、
DSL は Python と R で使用されます。DSL 構文スタイルを使用すると、一時ビューを作成する必要がなくなります。
(1)、DataFrameを作成します
val df = spar.read.json("/usr/local/soft/data/user.json")
(2) DataFrameのスキーマ情報を参照する
df.printSchem
(3)、「年齢」列のデータのみを表示します。
//df. //tab キーで df 内のメソッドを表示します
df.select("年齢").show()
(4) 「ユーザー名」列のデータと「年齢+1」のデータを表示する
df.select("age" + 1).show() // エラーレポート
df.select($"年齢" + 1).show()
df.select('年齢 + 1).show()
df.select($"ユーザー名",$"年齢" + 1).show()
df.select($"username",$"age" + 1 as "newage").show() // 取别名
注: 操作に関しては、各列で $ を使用するか、単一引用符 + フィールド名のように引用符式を使用する必要があります。
(5).「年齢」が「30」以上のデータを表示する
df.filter($"年齢" > 20).show()
df.filter($"年齢" > 30).show()
⑥「年齢」ごとにグループ化してデータ件数を表示
df.groupBy("年齢").count.show()

RDD をデータフレームに変換&データフレームをRDDに変換

IDEで開発する場合、RDDとDFまたはDSの間で動作する必要がある場合、インポートを導入する必要があります。
スパーク.implicits._
ここでのsparkはscalaのパッケージ名ではなく、作成されたsparkSessionオブジェクトの変数名であるため、
SparkSession オブジェクトを最初に作成してからインポートする必要があります。ここでの Spark オブジェクトは、Scala のみであるため、var を使用して宣言できません。
val で変更されたオブジェクトの導入をサポートします。
注: Spark-Shell ではインポートは必要ありません。これは自動的に行われます。
val rdd = sc.makeRDD(リスト(1,2,3,4))
//rdd. //tab キーで rdd のすべてのメソッドを表示します
val df = rdd.toDF("id")
df.show()
//df. //Tab キーで df のすべてのメソッドを表示します
df.rdd

データセット

DataSet は厳密に型指定されたデータ コレクションであるため、対応する型情報を提供する必要があります。

データセットの作成_

(1) サンプルクラスシーケンスを使用してDataSetを作成する
//ケースクラス PERSO(name:String,age:Long)
val list = List(ペルソ("zhangsan",30),パーソン("lisi",20))
val ds = list.toDS
ds.show

DataFrameからDataSet、DataSetからDataFrame

//df // dfが存在する かどうかを確認する
//ケースクラス Emp(年齢:Long,ユーザー名:String)
//val ds = df.as[Emp]
//ds.show()
//ds. //Tab キーの表示方法
//ds.toDF

RDDから DataSet&DataSetからRDD

val rdd = sc.makeRDD(List(Emp(30,"zhangsan"),Emp(20,"lisi")))
rdd.toDS
// 前提条件: 型を準備するにはサンプル クラスである必要があります
val rdd = sc.makeRDD(リスト(1,2,3,4))
rdd.toDS
val rdd = sc.makeRDD(List(Emp(30,"zhangsan"),Emp(20,"lisi")))
val ds = rdd.toDS
val rdd1 = ds.rdd

RDD DataFrame DataSetの関係

SarpkSQL では、Spark は DataFrame と DataSet という 2 つの新しい抽象化を提供します。

バージョンの違い

バージョンによると:
Spark1.0 ---->RDD
Spark1.3 ---->データフレーム
Spark1.6 ---->データセット
これら 3 つのデータ構造に同じデータが与えられた場合、計算後の結果はそれぞれ同じになります。違いは
いかに効率的に、そしてどのように実行されるか。以降の Spark バージョンでは、DataSet が段階的に RDD を置き換える可能性があります。
DataFrame が唯一の API インターフェイスになります。

3つの共通点

1. RDD、DataFrame、および Dataset はすべて、Spark プラットフォームの下にある分散型エラスティック データ セットであり、非常に大きなデータを処理するように設計されています。
利便性が提供されます。
2. これら 3 つはいずれも遅延機構を備えており、map メソッドなどの作成および変換の際には、すぐには実行されず、遭遇した場合にのみ実行されます。
Action が foreach のような場合、3 つはトラバース操作を開始します。
3. いずれもsparkのメモリに応じて操作を自動的にキャッシュするため、データ量が多くてもメモリ不足の心配がありません。
オーバーフロー。
4. 3 つすべてにパーティションの概念があります。
5. これら 3 つは、フィルター、並べ替えなどの多くの共通機能を備えています。
6. DataFrame および Dataset の多くの操作には、このパッケージのサポートが必要です: import
スパーク.implicits._。
7. DataFrame と Dataset は両方とも、パターン マッチングを使用して各フィールドの値と型を取得できます。

3つの違い

1.RDD:
(1) RDD は通常、spark mlib と併用されます。
(2) RDD は、sparksql 操作をサポートしていません。
2.データフレーム:
(1). RDDやDatasetとは異なり、DataFrameの各行の型はRowに固定されており、各列の値を直接指定することはできません。
Access では、各フィールドの値は解析によってのみ取得できます。
(2) DataFrame と Dataset は通常、spark mlib と一緒には使用されません。
(3)、DataFrame と Dataset はどちらも select、groupby などの sparsql 操作をサポートしており、次の点にも注意することができます。
一時テーブル/ウィンドウを登録し、SQL ステートメントの操作を実行します。
(4) DataFrame と Dataset は、CSV への保存など、テーブルとともに使用できるいくつかの特に便利な保存方法をサポートしています。
ヘッダーにより、各列のフィールド名が一目でわかるようになります。
3.データセット:
(1)、Dataset と DataFrame はまったく同じメンバー関数を持ち、唯一の違いは各行のデータ型です。
(2) DataFrame は Dataset[Row] とも呼ばれます。各行の型は Row です。解析されません。各行は正確には何を持っていますか?
フィールドの型や各フィールドの型を知る方法はなく、前述の getAS メソッドか 7 番目の共通点を使用するしかありません。
前述のパターンは一致して特定のフィールドを取り出します。Datasetでは各行の型が不定です。
ケースクラスを追加すると、各行の情報を自由に取得できるようになります。

IDEA が SparkSQL プログラムを作成する

実際の開発ではIDEAを利用して開発を行います。IDEA でのプログラムのパッケージ化と実行方法は SparkCore と同じです
同様に、新しい依存関係を Maven 依存関係に追加する必要があります。
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-sql -->
<依存関係>
<グループID> org.apache.spark </グループID>
<artifactId> スパーク-sql_2.12 </artifactId>
<バージョン > 2.4.5 </バージョン>
</依存関係>
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)
}

ユーザー定義関数

ユーザーは、spark.udf 関数を通じてカスタム関数を追加し、カスタム関数を実装できます。
UDF

1.データフレームの作成

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()
}
}
強く型指定された DataSet と弱く型指定された DataFrame は両方とも、count() などの関連する集計関数を提供します。
countDistinct()、avg()、max()、min()。さらに、ユーザーは独自のカスタム集計関数を設定できます。以下のように
UserDefinedAggregateFunction を継承して、ユーザー定義の弱い型指定の集計関数を実装します。Spark3.0バージョン以降
UserDefinedAggregateFunction は推奨されなくなり、厳密に型指定された集計関数を均一に使用できるようになりました。
アグリゲータ。

2.ユーザ定義の集計関数(弱い型)

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
}
}
ユーザー定義の集計関数 (spark3.0.0 以降の強力な型)
注: Spark3.0.0 には、 functions.udaf がありません。これを使用したい場合は、 pomファイル内のSparkバージョンを変更する必要があります
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
}
}

データのロードと保存

ユニバーサルなロードおよび保存方法

SparkSQL は、データを保存およびロードするための一般的な方法を提供します。ここでの共通とは、同じ API、root を使用することを指します。
さまざまなパラメーターに従って、さまざまな形式でデータを読み取り、保存します。SparkSQL によって読み取られ、保存されるデフォルトのファイル形式は次のとおりです。
寄木細工。
(1)データのロード
smile.read.load は データをロードするための一般的なメソッドです
Spark . read . //tab キーの閲覧方法
異なる形式のデータを読み取る場合は、異なるデータ形式を設定できます。
スパーク 読んでください 形式 ( "..." )[. オプション ( "..." )]。 ロード ( "..." )
1. format("..."): 「csv」、「jdbc」、「json」、「orc」、「parquet」、「textFile」など、ロードするデータ型を指定します。
2.load("..."): 「csv」、「jdbc」、「json」、「orc」、「parquet」、および「textFile」形式では、データをロードするためのパスを渡す必要があります。
3. option("..."): 「jdbc」形式では、対応する JDBC パラメータ、URL、ユーザー、パスワード、dbtable を渡す必要があります。最初に読み取り API を使用してファイルを DataFrame にロードし、実際、ファイルに対して直接クエリを実行することもできます。
// ファイル 形式.` ファイルパス` _
// user.json をHDFS アップロード _
//hadoop fs -put user.json /data
スパーク sql ( "select * from json.`data/user.json`" )。 見せる
(2) セーブデータ
df.write.save は データを保存するための一般的な方法です
df . write . //Tab キーの表示方法
DF 書きます 形式 ( "json" )。 保存 ( "/data/output1" )
異なる形式でデータを保存する場合は、異なるデータ形式を設定できます。
DF 書きます 形式 ( "..." )[. オプション ( "..." )]。 保存 ( "..." )
1. format("..."): ロードするデータ型を指定します。
括弧「csv」、「jdbc」、「json」、「orc」、「parquet」および「textFile」。
2.load("..."): "csv"、"jdbc"、"json"、"orc"、"parquet"、および "textFile" 形式で渡す必要があります
データをロードするパスを入力します。
3. option("..."): 「jdbc」形式では、対応する JDBC パラメータ、URL、ユーザー、パスワード、および
dbtable の保存操作では、SaveMode を使用してデータの処理方法を指定できます。設定するには、mode() メソッドを使用します。
セット。
注: これらの SaveMode はロックされておらず、アトミック操作ではありません。
SaveMode は、次の定数を含む列挙型クラスです。
DF 書きます モード ( 「追加」 )。 json ( "データ/出力1" )
DF 書きます モード ( 「上書き」 )。 json ( "データ/出力1" )
DF 書きます モード ( 「無視」 )。 json ( "データ/出力1" )

寄木細工の床

SparkSQL のデフォルトのデータ ソースは寄木細工形式です。parquet は、ネストされたデータを効率的に保存できる列型ストレージです。
フォーマット。
データ ソースが寄せ木細工のファイルである場合、SparkSQL はフォーマットを使用せずにすべての操作を簡単に実行できます。改訂
構成項目spark.sql.sources.defaultは、デフォルトのデータソース形式を変更できます。
(1)データのロード
//ファイルを HDFS にアップロードする
//hadoop fs -put users.parquet /data
val df = スパーク 読んでください ロード ( "/data/users.parquet" )
DF 見せる
(2)セーブデータ
val df = スパーク 読んでください json ( "data/user.json" )
DF 書きます モード ( 「追加」 )。 保存 ( "/data/output2" )

JSON

SparkSQL は、JSON データセットの構造を自動的に推論し、DataSet[ROW] としてロードできます。通過できる
SparkSession.read.json() を使用して JSON ファイルをロードします。
注: Spark によって読み取られる JSON ファイルは従来の JSON ファイルではなく、各行は JSON 文字列である必要があります。形式は次のようなものです
下:
{ "名前" : "張三" }
{ "名前" : "リシ" "年齢" : 21 }
{ "名前" : "王武" "年齢" : 24 },{ "名前" : "趙立" "年齢" : 23 }
(1)暗黙的な変換をインポートする
インポート スパーク 暗黙的に _
(2) JSONファイルの読み込み
val パス = "データ/user.json"
val userDF = スパーク 読んでください json ( パス )
(3) 一時テーブルを作成する
ユーザーDF createOrReplaceTempView ( "ユーザー" )
(4)データクエリ
スパーク sql ( "select * from user" )。 見せる

CSV

SparkSQL は、CSV ファイルのリスト情報を構成し、CSV ファイルを読み取り、CSV ファイルの最初の行をデータ列として設定できます
//ファイルを HDFS にアップロードする
//hadoop fs -put people.csv /data
val df_csv =
スパーク 読んでください 形式 ( 「csv」 )。 オプション ( "seq" ";" )。 オプション ( "inferSchema" "true" )
オプション ( "header" "true" )。 ロード ( "/data/people.csv" )

MySQL

SparkSQL は、JDBC を介してリレーショナル データベースからデータを読み取ることで DataFrame を作成できます。
DataFrame での一連の計算の後、データをリレーショナル データベースに書き戻すことができます。スパークシェル操作を使用する場合
これを行うには、シェルの起動時に関連するデータベース ドライバーのパスを指定するか、関連するデータベース ドライバーを Spark クラスに追加します。
パスの下にあります
ビン / スパーク - シェル
--jars mysql - connector - java - 5.1 27 - ビン
IDEAではJDBCを介してMysqlを操作しています。
(1)依存関係をインポートする
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<依存関係>
<グループID> mysql </グループID>
<artifactId> mysql-connector-java </artifactId>
<バージョン > 5.1.49 </バージョン>
</依存関係>
(2)読み出しデータ
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()
}
}

ハイブ

ApacheHive は Hadoop 上の SQL エンジンであり、SparkSQL は Hive サポートの有無にかかわらずコンパイルできます。
Hive サポートを含む SparkSQL は、Hive アクセス、UDF (ユーザー定義関数)、および Hive クエリ言語をサポートできます。
(HiveQL/HQL) など 強調する必要があるのは、Hive ライブラリを Spark SQL に含める場合は何もする必要がないことです。
まず Hive をインストールします。一般に、Spark SQL をコンパイルするときに Hive サポートを導入して、これを使用できるようにするのが最善です。
いくつかの特徴。Spark のバイナリ バージョンをダウンロードした場合は、Hive サポートを追加してコンパイルされているはずです。
(1)内蔵ハイブ
// 組み込み ハイブ
//スパークシェル
// まず、 spark-localフォルダー 内の metastore_db を削除します。
// 再度実行
スパーク SQL ( "テーブルを表示" )。 見せる
// Spark-localフォルダーを再度 確認します
val df = スパーク 読んでください json ( "data/user.json" )
DF createOrReplaceTempView ( "ユーザー" )
// 一時テーブルがあることがわかります。
スパーク SQL ( "テーブルを表示" )。 見せる
// Shujiaテーブル を作成する
スパーク sql ( "テーブルShujia(id int)の作成" )
// データをロードする
//データをロードする前に、 データフォルダーにid.txtファイルを作成する 必要があります
スパーク sql ( "パス 'data/id.txt' のローカル データをテーブル Shujia にロードします" )
// クエリ
スパーク sql ( "select * from Shujia" )。 見せる
実際の開発では、組み込みの Hive を使用する人はほとんどいません。
(2)外部ハイブ
外部にデプロイされた Hive に接続する場合は、次の手順を実行する必要があります。
(1) Hive を引き継ぐには、Spark は hive-site.xml ファイルを conf/ ディレクトリにコピーする必要があります。
[root@master conf] # cp hive-site.xml /usr/local/soft/spark-local/conf/
(2) mysql ドライバーを jars/ ディレクトリにコピーします。
[root@master パッケージ] # cp mysql-connector-java-5.1.49.jar /usr/local/soft/spark-local/jars/
(3) hdfs にアクセスできない場合は、core-site.xml と hdfs-site.xml を conf/ ディレクトリにコピーする必要があります。
(4) スパークシェルを再起動する
ビン / スパーク - シェル
(5) コード操作 Hive
依存関係を追加します。
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-hive -->
<依存関係>
<グループID> org.apache.spark </グループID>
<artifactId> スパークハイブ_2.12 </artifactId>
<バージョン > 2.4.5 </バージョン>
</依存関係>
<!-- https://mvnrepository.com/artifact/org.apache.hive/hive-exec -->
<依存関係>
<グループID> org.apache.hive </グループID>
<artifactId> ハイブ実行 </artifactId>
<バージョン > 1.2.1 </バージョン>
</依存関係>

コードを書きます:

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

以下は、特定のエラー報告に対する解決策です。

//インスタンス化中にエラーが発生しました
「org.apache.spark.sql.hive.HiveSessionState」:
java.lang.RuntimeException: ルート スクラッチ ディレクトリ: HDFS 上の /tmp/hive
書き込み可能である必要があります。現在の権限は次のとおりです: ---------
% 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
// エラーが発生しました: ハイブメタデータ データベース の文字セット エンコーディングを変更します
mysql > データベース ハイブ文字セット latin1 を変更します

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

おすすめ

転載: blog.csdn.net/qq_40322236/article/details/128903588