问题导读

1、什么是SparkContext?

2、如何配置Parquet?

3、如何高效的从Apache Hive中读出和写入数据?



(一)开始

Spark中所有相关功能的入口点是

SQLContext

类或者它的子类, 创建一个SQLContext的所有需要仅仅是一个SparkContext。

    1.  val sc: SparkContext // An existing SparkContext.
    2.  val sqlContext = new org.apache.spark.sql.SQLContext(sc)
    3.  
    4.  // createSchemaRDD is used to implicitly convert an RDD to a SchemaRDD.
    5.  import sqlContext.createSchemaRDD

    除了一个基本的SQLContext,你也能够创建一个HiveContext,它支持基本SQLContext所支持功能的一个超集。它的额外的功能包括用更完整的HiveQL分析器写查询去访问HiveUDFs的能力、 从Hive表读取数据的能力。用HiveContext你不需要一个已经存在的Hive开启,SQLContext可用的数据源对HiveContext也可用。HiveContext分开打包是为了避免在Spark构建时包含了所有 的Hive依赖。如果对你的应用程序来说,这些依赖不存在问题,Spark 1.2推荐使用HiveContext。以后的稳定版本将专注于为SQLContext提供与HiveContext等价的功能。

    用来解析查询语句的特定SQL变种语言可以通过spark.sql.dialect选项来选择。这个参数可以通过两种方式改变,一种方式是通过setConf方法设定,另一种方式是在SQL命令中通过SET key=value 来设定。对于SQLContext,唯一可用的方言是“sql”,它是Spark SQL提供的一个简单的SQL解析器。在HiveContext中,虽然也支持"sql",但默认的方言是“hiveql”。这是因为HiveQL解析器更 完整。在很多用例中推荐使用“hiveql”。



    (二)RDDs

    Spark支持两种方法将存在的RDDs转换为SchemaRDDs。第一种方法使用反射来推断包含特定对象类型的RDD的模式(schema)。在你写spark程序的同时,当你已经知道了模式,这种基于反射的 方法可以使代码更简洁并且程序工作得更好。

    创建SchemaRDDs的第二种方法是通过一个编程接口来实现,这个接口允许你构造一个模式,然后在存在的RDDs上使用它。虽然这种方法更冗长,但是它允许你在运行期之前不知道列以及列 的类型的情况下构造SchemaRDDs。

    利用反射推断模式

    Spark SQL的Scala接口支持将包含样本类的RDDs自动转换为SchemaRDD。这个样本类定义了表的模式。

    给样本类的参数名字通过反射来读取,然后作为列的名字。样本类可以嵌套或者包含复杂的类型如序列或者数组。这个RDD可以隐式转化为一个SchemaRDD,然后注册为一个表。表可以在后续的 sql语句中使用。

    1.  // sc is an existing SparkContext.
    2.  val sqlContext = new org.apache.spark.sql.SQLContext(sc)
    3.  // createSchemaRDD is used to implicitly convert an RDD to a SchemaRDD.
    4.  import sqlContext.createSchemaRDD
    5.  
    6.  // Define the schema using a case class.
    7.  // Note: Case classes in Scala 2.10 can support only up to 22 fields. To work around this limit,
    8.  // you can use custom classes that implement the Product interface.
    9.  case class Person(name: String, age: Int)
    10.  
    11.  // Create an RDD of Person objects and register it as a table.
    12.  val people = sc.textFile("examples/src/main/resources/people.txt").map(_.split(",")).map(p => Person(p(0), p(1).trim.toInt))
    13.  people.registerTempTable("people")
    14.  
    15.  // SQL statements can be run by using the sql methods provided by sqlContext.
    16.  val teenagers = sqlContext.sql("SELECT name FROM people WHERE age >= 13 AND age <= 19")
    17.  
    18.  // The results of SQL queries are SchemaRDDs and support all the normal RDD operations.
    19.  // The columns of a row in the result can be accessed by ordinal.
    20.  teenagers.map(t => "Name: " + t(0)).collect().foreach(println)




    编程指定模式

    当样本类不能提前确定(例如,记录的结构是经过编码的字符串,或者一个文本集合将会被解析,不同的字段投影给不同的用户),一个SchemaRDD可以通过三步来创建。

    从原来的RDD创建一个行的RDD

    创建由一个StructType表示的模式与第一步创建的RDD的行结构相匹配

    在行RDD上通过applySchema方法应用模式

    1.  // sc is an existing SparkContext.
    2.  val sqlContext = new org.apache.spark.sql.SQLContext(sc)
    3.  
    4.  // Create an RDD
    5.  val people = sc.textFile("examples/src/main/resources/people.txt")
    6.  
    7.  // The schema is encoded in a string
    8.  val schemaString = "name age"
    9.  
    10.  // Import Spark SQL data types and Row.
    11.  import org.apache.spark.sql._
    12.  
    13.  // Generate the schema based on the string of schema
    14.  val schema =
    15.    StructType(
    16.      schemaString.split(" ").map(fieldName => StructField(fieldName, StringType, true)))
    17.  
    18.  // Convert records of the RDD (people) to Rows.
    19.  val rowRDD = people.map(_.split(",")).map(p => Row(p(0), p(1).trim))
    20.  
    21.  // Apply the schema to the RDD.
    22.  val peopleSchemaRDD = sqlContext.applySchema(rowRDD, schema)
    23.  
    24.  // Register the SchemaRDD as a table.
    25.  peopleSchemaRDD.registerTempTable("people")
    26.  
    27.  // SQL statements can be run by using the sql methods provided by sqlContext.
    28.  val results = sqlContext.sql("SELECT name FROM people")
    29.  
    30.  // The results of SQL queries are SchemaRDDs and support all the normal RDD operations.
    31.  // The columns of a row in the result can be accessed by ordinal.
    32.  results.map(t => "Name: " + t(0)).collect().foreach(println)



    (三)Parquet文件

    Parquet是一种柱状(columnar)格式,可以被许多其它的数据处理系统支持。Spark SQL提供支持读和写Parquet文件的功能,这些文件可以自动地保留原始数据的模式。

    加载数据

    1.  // sqlContext from the previous example is used in this example.
    2.  // createSchemaRDD is used to implicitly convert an RDD to a SchemaRDD.
    3.  import sqlContext.createSchemaRDD
    4.  
    5.  val people: RDD[Person] = ... // An RDD of case class objects, from the previous example.
    6.  
    7.  // The RDD is implicitly converted to a SchemaRDD by createSchemaRDD, allowing it to be stored using Parquet.
    8.  people.saveAsParquetFile("people.parquet")
    9.  
    10.  // Read in the parquet file created above.  Parquet files are self-describing so the schema is preserved.
    11.  // The result of loading a Parquet file is also a SchemaRDD.
    12.  val parquetFile = sqlContext.parquetFile("people.parquet")
    13.  
    14.  //Parquet files can also be registered as tables and then used in SQL statements.
    15.  parquetFile.registerTempTable("parquetFile")
    16.  val teenagers = sqlContext.sql("SELECT name FROM parquetFile WHERE age >= 13 AND age <= 19")
    17.  teenagers.map(t => "Name: " + t(0)).collect().foreach(println)




    配置

    可以在SQLContext上使用setConf方法配置Parquet或者在用SQL时运行SET key=value命令来配置Parquet。

    spark权威指南中文版 pdf spark中文手册_sql

    (四)JSON数据集

    Spark SQL能够自动推断JSON数据集的模式,加载它为一个SchemaRDD。这种转换可以通过下面两种方法来实现

    • jsonFile :从一个包含JSON文件的目录中加载。文件中的每一行是一个JSON对象
    • jsonRDD :从存在的RDD加载数据,这些RDD的每个元素是一个包含JSON对象的字符串


    注意,作为jsonFile的文件不是一个典型的JSON文件,每行必须是独立的并且包含一个有效的JSON对象。结果是,一个多行的JSON文件经常会失败

    1.  // sc is an existing SparkContext.
    2.  val sqlContext = new org.apache.spark.sql.SQLContext(sc)
    3.  
    4.  // A JSON dataset is pointed to by path.
    5.  // The path can be either a single text file or a directory storing text files.
    6.  val path = "examples/src/main/resources/people.json"
    7.  // Create a SchemaRDD from the file(s) pointed to by path
    8.  val people = sqlContext.jsonFile(path)
    9.  
    10.  // The inferred schema can be visualized using the printSchema() method.
    11.  people.printSchema()
    12.  // root
    13.  //  |-- age: integer (nullable = true)
    14.  //  |-- name: string (nullable = true)
    15.  
    16.  // Register this SchemaRDD as a table.
    17.  people.registerTempTable("people")
    18.  
    19.  // SQL statements can be run by using the sql methods provided by sqlContext.
    20.  val teenagers = sqlContext.sql("SELECT name FROM people WHERE age >= 13 AND age <= 19")
    21.  
    22.  // Alternatively, a SchemaRDD can be created for a JSON dataset represented by
    23.  // an RDD[String] storing one JSON object per string.
    24.  val anotherPeopleRDD = sc.parallelize(
    25.    """{"name":"Yin","address":{"city":"Columbus","state":"Ohio"}}""" :: Nil)
    26.  val anotherPeople = sqlContext.jsonRDD(anotherPeopleRDD)



    (五)Hive表

    Spark SQL也支持从Apache Hive中读出和写入数据。然而,Hive有大量的依赖,所以它不包含在Spark集合中。可以通过-Phive和-Phive-thriftserver参数构建Spark,使其 支持Hive。注意这个重新构建的jar包必须存在于所有的worker节点中,因为它们需要通过Hive的序列化和反序列化库访问存储在Hive中的数据。

    当和Hive一起工作是,开发者需要提供HiveContext。HiveContext从SQLContext继承而来,它增加了在MetaStore中发现表以及利用HiveSql写查询的功能。没有Hive部署的用户也 可以创建HiveContext。当没有通过hive-site.xml配置,上下文将会在当前目录自动地创建metastore_db和warehouse。

    1.  // sc is an existing SparkContext.
    2.  val sqlContext = new org.apache.spark.sql.hive.HiveContext(sc)
    3.  
    4.  sqlContext.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
    5.  sqlContext.sql("LOAD DATA LOCAL INPATH 'examples/src/main/resources/kv1.txt' INTO TABLE src")
    6.  
    7.  // Queries are expressed in HiveQL
    8.  sqlContext.sql("FROM src SELECT key, value").collect().foreach(println)