spark SQL的DataFrame的操作以及和RDD的转换
相关概念:
spark的核心是RDD,它是弹性分布式数据集,对应着一系列的操作。Spark SQL是spark中数据处理的的一个模块,提供了抽象的数据操作方法,可以分布式的查询数据集,叫做DataFrame。另外,sparkSQL还可以从现有的数据集例如从hive种直接读取数据,具体的可以去查看这里查看。DataFrame是一个分布式以列名为为组织的数据集集合,在本质上也相当于数据库中的一个数据表,与之不同的是他又自己一系列的优化方法共大家使用,数据结构的文件(json格式),hive中的table,外部数据集,现有的RDD都可以产生DataFrame。spark提供了很大的方便来运行代码,我们可以在spark-shell中来看看具体的应用。
首先最主要的任务当然是创建sparkContext(sc),sparkSql,这两个最主要的成员变量,代码示例如下。
<span style="font-size:18px;">val sc: SparkContext
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
import sqlContext.implicits._</span>
隐士转换的就是用来把现存的RDD转换到DataFrame。当然,你也可以创建一个hiveContext变量,它是sqlContext的父类,具体的有关的hiveContext在这里就不多说了。
下面来看看DataFrame的具体操作,这里涉及了dataFrame 的创建和操作方法。
val sc: SparkContext
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
// dataframe的创建,从json中创建的
val df = sqlContext.read.json("examples/src/main/resources/people.json")
// Show 方法显示 DataFrame的内容
df.show()
// age name
// null Michael
// 30 Andy
// 19 Justin
// 以树状的形式打印模式,模式很重要,后面的另外两种方法会用到
df.printSchema()
// root
// |-- age: long (nullable = true)
// |-- name: string (nullable = true)
// Select 方法显示固定的列名
df.select("name").show()
// name
// Michael
// Andy
// Justin
df.select(df("name"), df("age") + 1).show()
// name (age + 1)
// Michael null
// Andy 31
// Justin 20
df.filter(df("age") > 21).show()
// age name
// 30 Andy
//以age分组时候显示做对应age 的个数
df.groupBy("age").count().show()
// age count
// null 1
// 19 1
// 30 1
RDD创建DataFrame
这里有两种方法使得从现有的RDD来转换到DataFrame。第一种方法是先创建一个case class,然后用反射的机制来读取相对应的参数来变成对应表格的列名,利用反射机制看以来特别的简洁以及当你知道已经现有的模式的时候可以很快的创建出来。第二种方法就不是用case class来提取参数,而是直接定义一个字符串来提取参数,因为有时候根本没有case class啊。实现一个编程接口然后apply到现有的RDD上去。这种方法比较有鲁棒性,因为当你在运行应用程序的时候依然不知道适应的参数类型的时候这种方法就不错的选择。
反射机制:
简单的来说,反射机制就是先定义一个case class,这个class包含若干个参数,然后利用反射机制来提取参数,就可以得到所对应的参数来当作table的列名。然而case class的好处不用多说大家都知道,注册了table之后,就可以进行相应的查询增添修改啦,哈哈。具体代码如下:
// sc is an existing SparkContext.
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
// this is used to implicitly convert an RDD to a DataFrame.
import sqlContext.implicits._
// Define the schema using a case class.
// Note: Case classes in Scala 2.10 can support only up to 22 fields. To work around this limit,
// you can use custom classes that implement the Product interface.
case class Person(name: String, age: Int)//注意啦,这里的参数就是所对应的列名
// Create an RDD of Person objects and register it as a table.
val people = sc.textFile("examples/src/main/resources/people.txt").map(_.split(",")).map(p => Person(p(0), p(1).trim.toInt)).toDF()//由RDD转换到DataFrame
people.registerTempTable("people")//注册table
// SQL statements can be run by using the sql methods provided by sqlContext.
val teenagers = sqlContext.sql("SELECT name, age FROM people WHERE age >= 13 AND age <= 19")
// The results of SQL queries are DataFrames and support all the normal RDD operations.
// The columns of a row in the result can be accessed by field index:
teenagers.map(t => "Name: " + t(0)).collect().foreach(println)
// or by field name:
teenagers.map(t => "Name: " + t.getAs[String]("name")).collect().foreach(println)
// row.getValuesMap[T] retrieves multiple columns at once into a Map[String, T]
teenagers.map(_.getValuesMap[Any](List("name", "age"))).collect().foreach(println)
// Map("name" -> "Justin", "age" -> 19)// getValuesMap[Any]方法
确定的模型编程接口:
第一步:这里的模型是从一个字符串表示的model来创建一个rows RDD
StructType
根据第一步创建的RDD去创建一个schema
createDataFrame(rowRDD,schema)
<span style="font-size:18px;">// sc is an existing SparkContext.
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
// Create an RDD
val people = sc.textFile("examples/src/main/resources/people.txt")
// The schema is encoded in a string
val schemaString = "name age"//字符串
// Import Row.
import org.apache.spark.sql.Row;
// Import Spark SQL data types
import org.apache.spark.sql.types.{StructType,StructField,StringType};
// Generate the schema based on the string of schema
val schema =//通过StructField由上面的字符串去创建一个schema
StructType(
schemaString.split(" ").map(fieldName => StructField(fieldName, StringType, true)))
// Convert records of the RDD (people) to Rows.//通过Row构造器把people对应的数据RDD变成rowRDD
val rowRDD = people.map(_.split(",")).map(p => Row(p(0), p(1).trim))
// Apply the schema to the RDD.
val peopleDataFrame = sqlContext.createDataFrame(rowRDD, schema)//创建table
// Register the DataFrames as a table.
peopleDataFrame.registerTempTable("people")//注册table
// SQL statements can be run by using the sql methods provided by sqlContext.
val results = sqlContext.sql("SELECT name FROM people")
// The results of SQL queries are DataFrames and support all the normal RDD operations.
// The columns of a row in the result can be accessed by field index or by field name.
results.map(t => "Name: " + t(0)).collect().foreach(println)</span>
上面介绍的就是初步的dataframe的创建以及简单的使用。个人感觉spark的速度的确是很快,但是在性能上还是如是mapReduce稳定,有时候会出现数据丢失。