spark内存计算框架
1、sparksql 操作hivesql
- 添加依赖
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.11</artifactId>
<version>2.3.3</version>
</dependency>
- 代码开发
package com.kaikeba.sql
import org.apache.spark.sql.SparkSession
//todo:利用sparksql操作hivesql
object HiveSupport {
def main(args: Array[String]): Unit = {
//1、构建SparkSession对象
val spark: SparkSession = SparkSession.builder()
.appName("HiveSupport")
.master("local[2]")
.enableHiveSupport() //开启对hive的支持
.getOrCreate()
//2、直接使用sparkSession去操作hivesql语句
//2.1 创建一张hive表
spark.sql("create table people(id string,name string,age int) row format delimited fields terminated by ','")
//2.2 加载数据到hive表中
spark.sql("load data local inpath './data/kaikeba.txt' into table people ")
//2.3 查询
spark.sql("select * from people").show()
spark.stop()
}
}
2、JDBC数据源(★★★★★)
- spark sql可以通过 JDBC 从关系型数据库中读取数据的方式创建DataFrame,通过对DataFrame一系列的计算后,还可以将数据再写回关系型数据库中
2.1 通过sparksql加载mysql表中的数据
- 添加mysql连接驱动jar包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
- 代码开发
import java.util.Properties
import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, SparkSession}
//todo:利用sparksql加载mysql表中的数据
object DataFromMysql {
def main(args: Array[String]): Unit = {
//1、创建SparkConf对象
val sparkConf: SparkConf = new SparkConf().setAppName("DataFromMysql").setMaster("local[2]")
//2、创建SparkSession对象
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
//3、读取mysql表的数据
//3.1 指定mysql连接地址
val url="jdbc:mysql://node03:3306/spark"
//3.2 指定要加载的表名
val tableName="user"
// 3.3 配置连接数据库的相关属性
val properties = new Properties()
//用户名
properties.setProperty("user","root")
//密码
properties.setProperty("password","123456")
val mysqlDF: DataFrame = spark.read.jdbc(url,tableName,properties)
//打印schema信息
mysqlDF.printSchema()
//展示数据
mysqlDF.show()
//把dataFrame注册成表
mysqlDF.createTempView("user")
spark.sql("select * from user where age >30").show()
spark.stop()
}
}
2.2 通过sparksql保存结果数据到mysql表中
- 代码开发(本地运行)
import java.util.Properties
import org.apache.spark.sql.{DataFrame, SparkSession}
//todo:通过sparksql把结果数据写入到mysql表中
object Data2Mysql {
def main(args: Array[String]): Unit = {
//1、创建SparkSession
val spark: SparkSession = SparkSession
.builder()
.appName("Data2Mysql")
.master("local[2]")
.getOrCreate()
//2、读取mysql表中数据
//2.1 定义url连接
val url="jdbc:mysql://node03:3306/spark"
//2.2 定义表名
val table="user"
//2.3 定义属性
val properties=new Properties()
properties.setProperty("user","root")
properties.setProperty("password","123456")
val mysqlDF: DataFrame = spark.read.jdbc(url,table,properties)
//把dataFrame注册成一张表
mysqlDF.createTempView("user")
//通过sparkSession调用sql方法
//需要统计经度和维度出现的人口总数大于1000的记录 保存到mysql表中
val result: DataFrame = spark.sql("select * from user where age > 30")
//保存结果数据到mysql表中
//mode:指定数据的插入模式
//overwrite: 表示覆盖,如果表不存在,事先帮我们创建
//append :表示追加, 如果表不存在,事先帮我们创建
//ignore :表示忽略,如果表事先存在,就不进行任何操作
//error :如果表事先存在就报错(默认选项)
result.write.mode("append").jdbc(url,"kaikeba",properties)
// result.write.mode(args(0)).jdbc(url,args(1),properties)
//关闭
spark.stop()
}
}
-
打成jar包集群提交
-
代码开发
import java.util.Properties import org.apache.spark.sql.{DataFrame, SparkSession} //todo:通过sparksql把结果数据写入到mysql表中 object Data2Mysql { def main(args: Array[String]): Unit = { //1、创建SparkSession val spark: SparkSession = SparkSession .builder() .appName("Data2Mysql") .getOrCreate() //2、读取mysql表中数据 //2.1 定义url连接 val url="jdbc:mysql://node03:3306/spark" //2.2 定义表名 val table="user" //2.3 定义属性 val properties=new Properties() properties.setProperty("user","root") properties.setProperty("password","123456") val mysqlDF: DataFrame = spark.read.jdbc(url,table,properties) //把dataFrame注册成一张表 mysqlDF.createTempView("user") //通过sparkSession调用sql方法 //需要统计经度和维度出现的人口总数大于1000的记录 保存到mysql表中 val result: DataFrame = spark.sql("select * from user where age >30") //保存结果数据到mysql表中 //mode:指定数据的插入模式 //overwrite: 表示覆盖,如果表不存在,事先帮我们创建 //append :表示追加, 如果表不存在,事先帮我们创建 //ignore :表示忽略,如果表事先存在,就不进行任何操作 //error :如果表事先存在就报错(默认选项) result.write.mode(args(0)).jdbc(url,args(1),properties) //关闭 spark.stop() } }
-
提交任务脚本
spark-submit \ --master spark://node01:7077 \ --class com.kaikeba.sql.Data2Mysql \ --executor-memory 1g \ --total-executor-cores 4 \ --driver-class-path /home/hadoop/mysql-connector-java-5.1.38.jar \ --jars /home/hadoop/mysql-connector-java-5.1.38.jar \ spark_class02-1.0-SNAPSHOT.jar \ append kaikeba
-
3. sparksql保存数据操作
- 代码开发
import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, SparkSession}
//todo:sparksql可以把结果数据保存到不同的外部存储介质中
object SaveResult {
def main(args: Array[String]): Unit = {
//1、创建SparkConf对象
val sparkConf: SparkConf = new SparkConf().setAppName("SaveResult").setMaster("local[2]")
//2、创建SparkSession对象
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
//3、加载数据源
val jsonDF: DataFrame = spark.read.json("E:\\data\\score.json")
//4、把DataFrame注册成表
jsonDF.createTempView("t_score")
//todo:5、统计分析
val result: DataFrame = spark.sql("select * from t_score where score > 80")
//保存结果数据到不同的外部存储介质中
//todo: 5.1 保存结果数据到文本文件 ---- 保存数据成文本文件目前只支持单个字段,不支持多个字段
result.select("name").write.text("./data/result/123.txt")
//todo: 5.2 保存结果数据到json文件
result.write.json("./data/json")
//todo: 5.3 保存结果数据到parquet文件
result.write.parquet("./data/parquet")
//todo: 5.4 save方法保存结果数据,默认的数据格式就是parquet
result.write.save("./data/save")
//todo: 5.5 保存结果数据到csv文件
result.write.csv("./data/csv")
//todo: 5.6 保存结果数据到表中
result.write.saveAsTable("t1")
//todo: 5.7 按照单个字段进行分区 分目录进行存储
result.write.partitionBy("classNum").json("./data/partitions")
//todo: 5.8 按照多个字段进行分区 分目录进行存储
result.write.partitionBy("classNum","name").json("./data/numPartitions")
spark.stop()
}
}
4. sparksql中自定义函数(★★★★★)
-
自定义UDF函数
-
代码开发
import org.apache.spark.sql.api.java.UDF1 import org.apache.spark.sql.types.StringType import org.apache.spark.sql.{DataFrame, SparkSession} //TODO:自定义sparksql的UDF函数 一对一的关系 object SparkSQLFunction { def main(args: Array[String]): Unit = { //1、创建SparkSession val sparkSession: SparkSession = SparkSession.builder().appName("SparkSQLFunction").master("local[2]").getOrCreate() //2、构建数据源生成DataFrame val dataFrame: DataFrame = sparkSession.read.text("E:\\data\\test_udf_data.txt") //3、注册成表 dataFrame.createTempView("t_udf") //4、实现自定义的UDF函数 //小写转大写 sparkSession.udf.register("low2Up",new UDF1[String,String]() { override def call(t1: String): String = { t1.toUpperCase } },StringType) //大写转小写 sparkSession.udf.register("up2low",(x:String)=>x.toLowerCase) //4、把数据文件中的单词统一转换成大小写 sparkSession.sql("select value from t_udf").show() sparkSession.sql("select low2Up(value) from t_udf").show() sparkSession.sql("select up2low(value) from t_udf").show() sparkSession.stop() } }
5. sparksql整合hive
- 步骤
- 1、需要把hive安装目录下的配置文件hive-site.xml拷贝到每一个spark安装目录下对应的conf文件夹中
- 2、需要一个连接mysql驱动的jar包拷贝到spark安装目录下对应的jars文件夹中
- 3、可以使用spark-sql脚本 后期执行sql相关的任务
- 启动脚本
spark-sql \
--master spark://node01:7077 \
--executor-memory 1g \
--total-executor-cores 4 \
--conf spark.sql.warehouse.dir=hdfs://node01:8020/user/hive/warehouse
- 应用场景
#!/bin/sh
#定义sparksql提交脚本的头信息
SUBMITINFO="spark-sql --master spark://node01:7077 --executor-memory 1g --total-executor-cores 4 --conf spark.sql.warehouse.dir=hdfs://node01:8020/user/hive/warehouse"
#定义一个sql语句
SQL="select * from default.hive_source;"
#执行sql语句 类似于 hive -e sql语句
echo "$SUBMITINFO"
echo "$SQL"
$SUBMITINFO -e "$SQL"
6. sparksql处理点击流日志数据案例(★★★★★)
-
需求描述
- 通过sparksql对用户访问产生点击流日志数据进行分析处理,计算出对应的指标
-
代码开发
-
(1)校验日志数据进行字段解析提取的工具类
- AccessLogUtils
import scala.util.matching.Regex case class AccessLog( ipAddress: String, // IP地址 clientId: String, // 客户端唯一标识符 userId: String, // 用户唯一标识符 serverTime: String, // 服务器时间 method: String, // 请求类型/方式 endpoint: String, // 请求的资源 protocol: String, // 请求的协议名称 responseCode: Int, // 请求返回值:比如:200、401 contentSize: Long, // 返回的结果数据大小 url:String, //访问的url地址 clientBrowser:String //客户端游览器信息 ) /** * 校验日志数据进行字段解析提取的工具类 */ object AccessLogUtils { val regex: Regex = """^(\S+) (\S+) (\S+) \[([\w:/]+\s[+\-]\d{4})\] "(\S+) (\S+) (\S+)" (\d{3}) (\d+) (\S+) (.*)""".r /** * 验证一下输入的数据是否符合给定的日志正则,如果符合返回true;否则返回false * * @param line * @return */ def isValidateLogLine(line: String): Boolean = { val options = regex.findFirstMatchIn(line) if (options.isEmpty) { false } else { true } } /** * 解析输入的日志数据 * * @param line * @return */ def parseLogLine(line: String): AccessLog = { // 从line中获取匹配的数据 val options = regex.findFirstMatchIn(line) // 获取matcher val matcher = options.get // 构建返回值 AccessLog( matcher.group(1), // 获取匹配字符串中第一个小括号中的值 matcher.group(2), matcher.group(3), matcher.group(4), matcher.group(5), matcher.group(6), matcher.group(7), matcher.group(8).toInt, matcher.group(9).toLong, matcher.group(10), matcher.group(11) ) } }
-
(2)指标统计
- LogAnalysis
import java.util.Properties import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.sql.{DataFrame, SparkSession} /** * 日志分析案例 */ object LogAnalysis { //定义url连接 val url="jdbc:mysql://node03:3306/spark" //定义属性 val properties=new Properties() properties.setProperty("user","root") properties.setProperty("password","123456") def main(args: Array[String]): Unit = { //1、构建sparkConf对象 val sparkConf: SparkConf = new SparkConf().setAppName("LogAnalysis").setMaster("local[2]") //2、构建sparkSession对象 val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate() //3、获取sparkContext对象 val sc: SparkContext = spark.sparkContext sc.setLogLevel("warn") //4、读取数据文件 val logRDD: RDD[String] = sc.textFile("./logs/access.log") //5、过滤脏数据,然后解析 val rightRDD: RDD[String] = logRDD.filter(line => AccessLogUtils.isValidateLogLine(line)) val accessLogRDD: RDD[AccessLog] = rightRDD.map(line => AccessLogUtils.parseLogLine(line)) //6、将RDD转换成DataFrame import spark.implicits._ val accessLogDF: DataFrame = accessLogRDD.toDF //7、将DataFrame注册成一张表 accessLogDF.createTempView("access") //todo:8、指标分析 //todo:8.1 求contentSize的平均值,最大值以及最小值 val result1 = spark.sql( """ |select |date_sub(from_unixtime(unix_timestamp(),'yyyy-MM-dd'),1) as time, |AVG(contentSize) as avg_contentSize, |MAX(contentSize) as max_contentSize, |MIN(contentSize) as min_contentSize |from access """.stripMargin) //展示结果数据 result1.show() //保存结果数据到mysql表中 //result1.write.jdbc(url,"t_contentSizeInfo",properties) //todo:8.2 求 pv 和 uv val result2 = spark.sql( """ |select |date_sub(from_unixtime(unix_timestamp(),'yyyy-MM-dd'),1) as time, |count(*) as pv, |count(distinct ipAddress) as uv |from access """.stripMargin) //展示结果数据 result2.show() //保存结果数据到mysql表中 // result2.write.jdbc(url,"t_uv_pv",properties) //todo:8.3 求各个响应码出现的次数 val result3 = spark.sql( """ |select |date_sub(from_unixtime(unix_timestamp(),'yyyy-MM-dd'),1) as time, |responseCode as code, |count(*) as count |from access |group by responseCode """.stripMargin) //展示结果数据 result3.show() //保存结果数据到mysql表中 //result3.write.jdbc(url,"t_responseCode",properties) //todo:8.4 求访问url次数最多的前N位 val result4 = spark.sql( """ |select |*,date_sub(from_unixtime(unix_timestamp(),'yyyy-MM-dd'),1) as time |from ( |select |url as url, |count(*) as count |from access |group by url) t |order by t.count desc limit 5 """.stripMargin) //展示结果数据 result4.show() //保存结果数据到mysql表中 //result4.write.jdbc(url,"t_url",properties) //todo:8.5 求各个请求方式出现的次数 val result5 = spark.sql( """ |select |date_sub(from_unixtime(unix_timestamp(),'yyyy-MM-dd'),1) as time, |method as method, |count(*) as count |from access |group by method """.stripMargin) //展示结果数据 result5.show() //保存结果数据到mysql表中 //result5.write.jdbc(url,"t_method",properties) spark.stop() } }
-