spark sql 性能技术简介:
1,内存列存储(in-memory columnar storage):Spark sql 的数据,不是使用 java 对象的方式来进行存储,而是使用了面向列的方式进行存储。每一列作为一个数据存储的单位,从而大大的优化了内存的使用效率,减少了对内存的消耗,也就避免了gc的大量数据的性能消耗
2,字节码生成技术(byte-core generation):spark sql 在其catalyst模块增加了codegen模块,对于SQL语句的计算表达式 select num +num from t这种sql,就可以使用动态字节码技术
3,Scala 代码的优化:对于scala代码编写中,可能会造成性能较大开销的地方,自己重写或者更改逻辑,使用更加复杂的方式,来获取性能提升
DataFrame:以列的形式组织的,分布式的数据集合.它和关系型数据库中的表类似,但是做了底层的优化.DataFrame可以通过很多源来进行构建,包括:结构化数据文件,Hive之中的表,外部的关系型数据库,RDD等
spark sql 常见的操作:
1,处理的文件 json_load.json (并不是json格式,可以根据hive load文件理解)
{"name": "小明","age": 13,"from":"heman"},
{"name": "小Q","age": 11,"from":"shanghai"},
{"name": "小G","age": 14,"from":"qingzang"}
2,scala 代码示意
package day02
import org.apache.spark.sql.SQLContext
import org.apache.spark.{SparkConf, SparkContext}
object sparksql {
def main(args: Array[String]){
//创建 DataFrame
val conf =new SparkConf().setAppName("SQL")
val sc =new SparkContext(conf)
val sqlContext =new SQLContext(sc)
conf.set("spark.testing.memory", "2147480000")
//创建的 DataFrame 可以理解一张表
val df = sqlContext.read.json("hdfs://master:9000/json_load.json")
// 打印 DataFrame所有数据
df.show()
/*
| 小明| 14|
| 小Q| 12|
| 小G| 15|
* */
// 打印 DataFrame元数据(Scheme)
df.printSchema()
/*
root
|-- age: long (nullable = true)
|-- from: string (nullable = true)
|-- name: string (nullable = true)
* */
// 查询某一列的所有数据
df.select("name").show()
/*
* 小明|
| 小Q|
| 小G|
* */
// 查询某几列数据 并且进行计算
df.select(df("name"),df("age")+1).show()
/*
+----+---------+
|name|(age + 1)|
+----+---------+
| 小明| 14|
| 小Q| 12|
| 小G| 15|
+----+---------+
* */
// 对于某列的值进行过滤
df.filter(df("age")>18).show()
// 某列的值进行 分组 然后尽心聚合
df.groupBy("age").count().show()
/*
| 11| 1|
| 13| 1|
| 14| 1|
*/}
}
RDD转换 DataFrame 的两种方式:
1,使用反射来推断包含了特定数据的RDD,此时是已经知道RDD的元数据
2,通过编程接口来创建 DataFrame(运行的过程才知道元数据),可以再程序运行的时候创建一份元数据,将其动态添加到已经存在的RDD元数据之中
使用反射创建RDD
package day02
import org.apache.spark.sql.SQLContext
import org.apache.spark.{SparkConf, SparkContext}
object RddToDataFrame extends App {
val conf = new SparkConf().setAppName("Frame").setMaster("local")
val sc = new SparkContext(conf)
val sqlcontext = new SQLContext(sc)
// scala 使用反射进行 rdd 转换 DataFrame需要使用隐式转换
import sqlcontext.implicits._
//相当于表的schema
case class Students(id:Int,name:String,age:Int)
// 元素为 case class 的RDD
// spark sql 会通过反射读取传递给 case class 的参数的名称,然后将其作为列明
val students =sc.textFile("/student.txt")
.map(line => line.split(","))
//将RDD和case class关联
.map(arr =>Students(arr(0).trim().toInt,arr(1),arr(2).trim().toInt))
//将RDD转换成DataFrame
val studentDF =students.toDF()
studentDF.registerTempTable("students")
val teenageDF = sqlcontext.sql("select * from students where age<18")
val teenagerRDD = teenageDF.rdd
teenagerRDD.map{row => Students(row(0).toString.toInt,row(1).toString,row(2).toString.toInt)}
.collect()
.foreach(stu=>println(stu.id+":"+stu.name+":"+stu.age))
// 使用 row 的getAs() 方法来获取指定的列名的列
teenagerRDD.map{row => Students(row.getAs[Int]("id"),row.getAs("name"),row.getAs("age"))}
.collect()
.foreach(stu=>println(stu.id+":"+stu.name+":"+stu.age))
// 通过 row 的 getValuesMap 方法获取指定的几列的值,返回的是 map
val t = teenagerRDD.map(row=>{
val map = row.getValuesMap[Any](Array("id","name","age"));
Students(map("id").toString().toInt,map("name").toString(),map("age").toString().toInt)
})
t.collect()
.foreach(stu=>println(stu.id+":"+stu.name+":"+stu.age))
}
通过编程接口来创建 DataFrame
package day02
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.{Row, SQLContext}
import org.apache.spark.sql.types.{StructType,StructField,StringType,IntegerType}
object RDDtoDataframe_Programe extends App {
val conf = new SparkConf().setMaster("local[*]")
.setAppName("RDD")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
// 构造元素为 Row的普通 RDD
val studentsRdd = sc.textFile("/student.txt",1)
.map(
line => Row( // Row 里面取出的数据是 string类型的数据
line.split(",")(0).toInt,
line.split(",")(1).toString,line.split(",")(2).toInt
))
// 以编程的方式构造元数据, id name age 字段,
// 可能是在程序运行的过程之中动态从数据库读取的,或者配置文件之中
val structType = StructType(Array(
StructField("id",IntegerType,true),
StructField("name",StringType,true),
StructField("age",IntegerType,true)))
// 第三步进行 RDD 到 DataFrame的转换
val studentsDF = sqlContext.createDataFrame(studentsRdd,structType)
studentsDF.registerTempTable("students")
val teenagerDF = sqlContext.sql("select * from students where id<18")
val teenagerRdd = teenagerDF.rdd.collect().foreach(row =>println(row))
}
spark 的 load与save操作:
1,对于 spark sql 的DataFrame 来说,无论从什么数据源创建的 DataFrame,都有共同的load 与 save操作.load操作主要是加载数据,创建出 DataFrame,save操作主要是讲 DataFrame 保存到文件(列存储)之中.
2,可以手动指定来操作数据源类型,指定load 数据源格式与save格式,可以使用这个功能进行数据源类型的转换
3,save操作可以指定 SaveMode 目标位置文件存在,如何进行处理
package day02
import org.apache.spark.sql.{SQLContext, SaveMode}
import org.apache.spark.{SparkConf, SparkContext}
object GenderLoadSave {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("LoadAndSave")
.setMaster("local[*]")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
// 加载文件 创建出DataFrame
val UserDf = sqlContext.read.load("hdfs://master:9000/users.parquet")
// 保存为到文件夹之中 SaveMode.ErrorIfExists 表明文件存在就抛出异常
UserDf.write.mode(SaveMode.ErrorIfExists)
.save("hdfs://master:9000/users_new_parquet.scala")
// 手动指定数据源格式 load数据源格式与 save数据源格式
val peopleDF = sqlContext.read.format("json").load("hdfs://master:9000/people.json")
//SaveMode.Append 表明如果目标位置已经存在文件,那么就追加进去
peopleDF.select("name").write.mode(SaveMode.Append)
.format("parquet").
save("hdfs://master:9000/people_json_parquet")
}}