spark sql 技术说明与常见的操作(其一)
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") }}