Spark SQL
DataFrame与RDD的区别
- DataFrame的推出,让Spark具备了处理大规模结构化数据的能力,不仅比原有的RDD转化方式更加简单易用,而且获得了更高的计算性能。
- Spark能够轻松实现从MySQL到DataFrame的转化,并且支持SQL查询
- RDD是分布式的Java对象的集合,但是,对象内部结构对于RDD而言却是不可知的。
- DataFrame是一种以RDD为基础的分布式数据集,提供了详细的结构信息。
DataFrame的创建
SparkSession实现了SQLContext及HiveContext的所有功能,支持从不同的数据源加载数据,并把数据转换成DataFrame,并且支持把DataFrame转换成SQLContext自身中的表,然后使用SQL语句来操作数据。SparkSession亦提供了HiveQL以及其他依赖于Hive的功能的支持。
import spark.implicts._ 支持RDDs转换为DataFrmaes及后续SQL操作,用于Scala中,
//以下两种命令在scala中可正确执行,但在pyspark中返回错误
df.select(df("name"), df("age")+1).show()
df.filter(df("age") > 20).show
pyspark中的命令:
df.select(df["name"], df["age"]+1).show()
df.filter(df["age"] > 20).show
理解:在pyspark中指明某一列如“age”,需使用df.age或df[“age”],而在scala中需使用df(“age”)
df.select(df("name").as("username"),df("age")).show()
pyspark中的命令:
1. as变为alias
df.select(df["name"].alias("username"), df.age).show()
2. 通过临时表,执行sql命令
>>> df.createOrReplaceTempView("people")
>>> sqlDF = spark.sql("SELECT name AS username , age FROM people")
>>> sqlDF.show()
该结果即sqlDF返回的是DataFrame
利用反射机制推断RDD模式(预先定义结构)
优点:代码简洁
缺点:需已知原数据
- 在利用反射机制推断RDD模式时,需要首先定义一个case class
- 必须注册一个临时表,才能进行sql语句查询
case class Person(name: String, age: Long)
val peopleDF = spark.sparkContext.textFile("file:///user/local/spark/examples/src/main/respirces/people.txt").map(_.spilt(",").map(attributes => Person(attributes(0),attributes(1).trim.toint)).toDF()
val personsRDD = spark.sql("select name,age from people where age > 20")
personsRDD.map(t => "Name:" + t(0) + "age:" + t(1)).show()
利用编程方式定义RDD模式(无法预先定义结构)
优点:无需提前获取元数据,可在程序在运行中获取元数据。
缺点:代码复杂
types支持生成关系模式的对象或变量,Row支持将每一行数据生成一个对象
import org.apache.spark.sql.types._
import org.apache.spark.sql.Row
val schemaString = "name age"
val fields = schemaString.split(" ").map(fieldName => StructField(fieldName, StringType, nullable=true)) //将结构字符串转换为数组,再将其转换为StructField对象。
val schema = StructType(fields) //schema描述了模式信息,模式中包含name和age两个字段
val rowRDD = peopleRDD.map(_.split(",").map(attriutes => Row(attributes(0),attributes(1).trim)) //对peopleRDD这个RDD中的每一行元素都进行解析
val peopleDF = spark.createDataFrame(rowRDD,schema)
将RDD保存为文件
val peopleDF = spark.read.format("json").load("file:///usr/local/spark/examples/main/resources/people.json")
方法一:peopleDF.select("name","age").write.format("file:///usr/local/spark/mycode/newpeople.csv")
方法二:
df.rdd.saveAsTextFile("file:///usr/local/spark/newpeople.txt")
读写Parquet
dfx想进行sql语句查询,必须首先创建临时表
val parquetFileDF = spark.read.parquet('file:///usr/local/spark/examples/src/main/resources/users.parquet")
peopleDF.write.qarquet("file:///usr/local/spark/mycode/users.qarquet")
通过JDBC连接数据库
1. 读数据
val jdbcDF = spark.read.format("jdbc").option("url","jdbc://localhost:3306/spark").option("driver","com.mysql.jdbc.Driver").option("dbtable","student").option("user","root").option("password","hadoop").load()
jdbcDF.show()
2. 写数据
import java.util.Properties
import org.apache.spark.sql.types._
import org.apache.spark.sql.Row
val studentRDD = spark.sparkContext.parallelize(Array("3 Rongcheng M 25","4 Guanhua M 25")).map(_.split(" ")) //将数组生成RDD,包含两个元素,每一行为一个元素
val schema = StructType(List(StructField("id",IntergerType,true),StructField("name",StringType,true),StructField("gender",StringType,true),StructField("age",IntergerType,true)))
val rowRDD = studentRDD.map(p => Row(p(0)).toint,p(1).trim,p(2).trim,p(3).toint))//将数组生成ROW对象
val studentDF = spark.createDataFrame(rowRDD,schema)
//创建一个prop变量用来保存JDBC连接参数
val prop = new Properties()
prop.put("user","root")
prop.put("password","hadoop")
prop.put("driver","com.mysql.jdbc.Driver"))
//下面就可以连接数据库,采用append模式,表示追加纪录到数据库spark的student表中
studentDF.write.mode("append").jdbc("jdbc:mysql://localhost:3306/spark","spark.student",prop)