Spark sql介绍
是Spark用来处理结构化数据的一个模块,它提供了一个编程抽象叫做DataFrame并且作为分布式SQL查询引擎的作用。(在Spark中使用sql查询)
- DataFrame可处理结构化数据,所以Spark sql中先将数据集转化为RDD再将RDD转化为DataFrames对象,之后使用sql查询数据。
Spark sql应用
一、 在Spark-shell中:SparkContext和SQLContext是创建好的 所以不需要创建
- 将数据集转换RDD(分布式数据集),再将RDD转换为DataFrames对象(保存数据信息和数据结构信息[相当于数据库中的表])
val seq= Seq(("1","xiaoming",15),("2","xiaohong",20),("3","xiaobi",10))----数据集存为List
val rdd1 = sc.parallelize(seq)----转换为RDD,实现并行化
rdd1.toDF----转换为DataFrames对象(空参下数据的列名以数字1依次递增)----查询时直接.show即可
val df = rdd1.toDF("id","name","age")----传入参数,参数依次对应数据集中的数据设为列名,因为指定了对象名故查询时使用df.show
- 转换为DataFrames之后就可以执行查询了:
查询时有两种查询风格:DSL风格查询(特定邻域下的语言)和SQL风格查询(在spark交互中使用SQ语句查询)
1.DSL风格查询:
查询所有:df.show
以列名查询:df.select("列名").show
条件过滤:df.select().filter().show----括号中是字符类型
查看DataFrames的数据结构:df.printSchema
2.SQL风格查询:先转换为临时表再查询
转换----将DataFrame注册成表(临时表),表会被存储:df.registerTempTable("临时表名")
查询----spark.sqlContext.sql("SQL语句").show
二、在IDEA中:
- 先导依赖:注意版本
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
- 方式一:通过toDF将RDD转化为dataframes,相当于反射,这里若要使用的话,需要导入包:new SQLContext(sc)下的。
- 直接调用toDF的时候,使用的就是默认列名:数字从1开始逐渐递增;在指定列名时列名要和数据一一对应
- 可以在调用toDF方法的时候指定类的名称(指定名称多余数据会报错)
//方式一:registerTempTable即将淘汰,横线划住
object SparkSqlTest {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[5]").setAppName("SparkSql")
val sc = new SparkContext(conf)
val lines = sc.textFile("D:\\data\\person.txt")
val value: RDD[Array[String]] = lines.map(line => {line.split(" ")})
// 将获取的数据关联到样例类中
val person: RDD[Person] = value.map(line => {Person(line(0).toInt,line(1).toString,line(2).toInt)})
person.foreach(x=>{println(x)})
// 使用sql语句查询
val sqlc = new SQLContext(sc)
import sqlc.implicits._
val frame: DataFrame = person.toDF()
frame.show()
frame.registerTempTable("t_person")
val sqls = "select * from t_person"
val frame1 = sqlc.sql(sqls)
frame1.show()
frame1.write.mode("append").save("D:\\data\\person\\result.txt")
sc.stop()
}
//样例类
case class Person(id:Int,name:String,age:Int)
}
注意点:
-
样例类:相当于new出的构造方法,在object中使用时不再需要new了。
使用代码编程时数据是存储到样例类中,样例类中的构造方法中的参数就是对应的列名,所以通过toDF可以直接获取对应的属性名作为列名使用;同时也可以自定义列名 -
写出:
model的参数:Overwrite --复写;Append ----- 追加
save的参数:写出的地址
- 方式二:通过createDataFrame将RDD转化为dataframes
- 先创建StructType对象
- 再将数据获取
- 最后传入参数转换为DataFrames
//方式二:StructType封装数据结构
object SqlTest{
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[5]").setAppName("SqlTest")
val sc = new SparkContext(conf)
val sqls = new SQLContext(sc)
val lines: RDD[String] = sc.textFile("D:\\data\\person.txt")
val word: RDD[Array[String]] = lines.map(_.split(" "))
// 封装数据结构
val structType: StructType = StructType {
List(
// 参数:列名 数据类型 是否可以为空
StructField("id", IntegerType, false),
StructField("name", StringType, true),
StructField("age", IntegerType, false)
)
}
val row: RDD[Row] = word.map(arr => Row(arr(0).toInt,arr(1),arr(2).toInt))
val frame: DataFrame = sqls.createDataFrame(row,structType)
frame.show()
sc.stop()
}
}
注意点:
- StructType对象:用于分装数据结构(StructField用于存放字段即列名)
- 其中定义的列名数据大于存放数据时,所对应列的值应该是null;定义的列名数据是不能小于存放数据的,不然会抛出异常
- 方式三:将结果放入数据库
//方式三:将结果放入到数据库
object WriteSqlTest{
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[5]").setAppName("WriteSqlTest")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
val line: RDD[Array[String]] = sc.textFile("D:\\data\\person.txt").map(_.split(" "))
val data: RDD[Row] = line.map(x => Row(x(0).toInt,x(1),x(2).toInt))
val structType: StructType = StructType {
Array(
StructField("id", IntegerType, false),
StructField("name", StringType, true),
StructField("age", IntegerType, false
)
)
}
val frame = sqlContext.createDataFrame(data,structType)
val properties: Properties = new Properties()
properties.put("user","root")
properties.put("password","1234")
properties.put("driver","com.mysql.cj.jdbc.Driver")
val jdbcurl = "jdbc:mysql://localhost/spark?characterEncoding=utf-8&serverTimezone=UTC"
val table = "person"
frame.write.mode("append").jdbc(jdbcurl,table,properties)
println("成功")
sc.stop()
}
}
注意点:
- 要导入mysql连接的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
- jdbc的参数:(url,table,properties)写出到MySQL
jdbcurl----------连接的数据库
table------------表名
Properties-------用于连接数据库,配置用户,密码,驱动程序
三、HIVE-on-Spark:Hive的底层是通过MapReduce进行计算,当spark使用hive时要将其改变为sparkCore来执行(由于MapReduce中间计算均需要写入磁盘,而Spark是放在内存中,所以总体来讲Spark比MapReduce快很多。)
-
该项目的目的是把Spark作为Hive的一个计算引擎,将Hive的查询作为Spark的任务提交到Spark集群上进行计算。通过该项目,可以提高Hive查询的性能,同时为已经部署了Hive或者Spark的用户提供了更加灵活的选择,从而进一步提高Hive和Spark的普及率。
-
在该项目下,使用Hive的标准在Spark集群上运行。
-
真正要计算的数据是保存在HDFS中的,mysql这个元数据库保存的是hive表的描述信息。
-
应用:
- 前期准备:
1.将hive安装路径下的hive-site.xml拷贝到spark的配置conf配置文件目录
2.不高可用机制下:将Hadoop安装目录中的core-site.xml拷贝到spark的配置conf文件目录下
高可用机制下:将hadoop安装路径下的core-site,xml和hdfs-site.xml拷到spark的conf目录下
3.将数据库连接的jar包mysql-connector-java-5.1.39.jar拷入到Linux下
4.准备数据源(/root/person.txt) - 启动集群在bin下启动spark-sql交互窗口,进入到spark-sql交互窗口后就可以使用hive标准查询数据了:
- 前期准备:
./spark-sql --master spark://hdp-0:7077(Spark的URL) --executor-memory 512m --total-executor-cores 2 --jars mysql连接的jar包(全路径) --driver-class-path jar包所在位置
建表:create table person(id int,name string,age int) row format delimited fields terminated by ' ';
加载数据:load data local inpath '/root/person.txt' into table person;
处理数据:sql语句