Spark SQL,DataFrame 和Datasets 指南--Spak2.4.3

Spark SQL,DataFrame,Dataset

Spark SQL是一个结构化数据处理模块。不像Spark RDD API,Spark SQL提供的接口使Spark更加熟悉数据和执行的计算的结构。在内部,Spark SQL使用额外的信息来执行额外的优化。可以通过SQL和Dataset API来和Spark SQL交互。不管使用什么API/语言来表达计算,都是用的一个计算引擎。这种统一给了开发者很大的自由度来在不同的API之间选择最容易表达计算转换方式的方式。
本页中所有的采样数据使用的都是采样数据,可以在spark-shell,pyspark,sparKR中运行。

SQL

Spark SQL用处之一就是执行SQL查询,还可以从HIve中读取数据。如果想直到如何配置这些功能请查阅Hive Tables部分。当使用另一种编程语言来运行SQL时返回结果时Dataset/DataFrame。还可以采用命令行甚至JDBC/ODBC来和SQL接口交互。

Datasets和DataFrame

Dataset是分布式数据集合,从Spark1.6开始支持,它拥有RDD的优势(强类型,使用lambda函数)以及Spark SQL优化过的执行引擎。Dataset可以来源于JVM对象,可以采用转换函数操作(map.flatMap,filter等)。Scala和Python支持Dataset,而Python不支持。但由于Python动态的特性很多优势已经有了(可以通过row.columnName获取某行的某列值)。R也类似。
DataFrame就是有命名列的Dataset。概念上它等价于关系型数据库中的表或Python/R中的data frame,而其中包含了更多的优化。DataFrame的构建来源很广:结构化数据文件,Hive表,外部数据库或者RDD。四种语言都支持DataFrame。Scala和Java中DataFrame由Row构成的Dataset。Scala中DataFrame就是Dataset[Row],Java中则是Dataset。
这篇文档中就把Row组成的Dataset当作DataFrame。

Start

出发吧:SparkSession

Spark中所有功能的入口是SparkSession类。要想创建基本的SparkSession可以直接使用SparkSession.builder:

from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("Python Spark SQL basic example").config("spark.some.config.option","some-value").getOrCreate()

所有的样例代码位于Sprak repo中的examples/src/main/python/sql/basic.py。Spark2.0的SparkSession内置支持使用HiveQL写查询,使用Hive UDF以及从Hive表中读取数据的功能。有了这些特性你就不必非得启动Hive了。

创建DataFrame

有了SparkSession,应用就可以由现存的RDD,Hive表或Spark数据源中创建DataFrame。
下面的例子是基于JSON文件创建DataFrame:

# spark is an existing SparkSession
df = spark.read.json("examples/src/main/resources/people.json")
# Displays the content of the DataFrame to stdout
df.show()
# +----+-------+
# | age|   name|
# +----+-------+
# |null|Michael|
# |  30|   Andy|
# |  19| Justin|
# +----+-------+

全部代码在Spark repo中的examples/src/main/python/sql/basic.py。

非泛型 Dataset操作(即DataFrame操作)

DataFrame实际是为Scala,Java,Python,R提供了处理结构化数据的领域内的语言。
如上所述,Spark2.0中,Java和Scala的DataFrame就是Row组成的Dataset。Scala/Java Dataset属于强泛型,而这种DataFrame就是非泛型操作了。
这里展示一些使用Dataset完成的结构化数据处理样例。
Python中可以通过属性或索引来获取DataFrame的列。尽管前者很方便,但是仍建议使用后者

# spark, df are from the previous example
# Print the schema in a tree format
df.printSchema()
# root
# |-- age: long (nullable = true)
# |-- name: string (nullable = true)

# Select only the "name" column
df.select("name").show()
# +-------+
# |   name|
# +-------+
# |Michael|
# |   Andy|
# | Justin|
# +-------+

# Select everybody, but increment the age by 1
df.select(df['name'], df['age'] + 1).show()
# +-------+---------+
# |   name|(age + 1)|
# +-------+---------+
# |Michael|     null|
# |   Andy|       31|
# | Justin|       20|
# +-------+---------+

# Select people older than 21
df.filter(df['age'] > 21).show()
# +---+----+
# |age|name|
# +---+----+
# | 30|Andy|
# +---+----+

# Count people by age
df.groupBy("age").count().show()
# +----+-----+
# | age|count|
# +----+-----+
# |  19|    1|
# |null|    1|
# |  30|    1|
# +----+-----+

DataFrame全部操作可见于API Document
除了上面的列引用和表达式,DataFrame有丰富的库函数用于处理字符串操作,日期计算,常用的数学运算等。完整列表见于DataFrame Function Reference

SQL查询

SparkSession的sql函数使应用能够运行SQL查询并返回DataFrame。

# Register the DataFrame as a SQL temporary view
df.createOrReplaceTempView("people")

sqlDF = spark.sql("SELECT * FROM people")
sqlDF.show()
# +----+-------+
# | age|   name|
# +----+-------+
# |null|Michael|
# |  30|   Andy|
# |  19| Justin|
# +----+-------+

全局临时视图

Spark SQL的临时视图属于会话期间有效,一旦会话结束则消失地无影无踪。如果想要创建一个会话间共享甚至Spark应用结束了还能访问的临时视图那就创建个全局临时视图吧。全局临时视图关联于系统保存的数据库global_temp,所以需要使用该名来索引:SELECT * FROM global_temp.view1

# Register the DataFrame as a global temporary view
df.createGlobalTempView("people")

# Global temporary view is tied to a system preserved database `global_temp`
spark.sql("SELECT * FROM global_temp.people").show()
# +----+-------+
# | age|   name|
# +----+-------+
# |null|Michael|
# |  30|   Andy|
# |  19| Justin|
# +----+-------+

# Global temporary view is cross-session
spark.newSession().sql("SELECT * FROM global_temp.people").show()
# +----+-------+
# | age|   name|
# +----+-------+
# |null|Michael|
# |  30|   Andy|
# |  19| Justin|
# +----+-------+

创建Dataset

Dataset类似于RDD,但不是使用Java序列化或Kryo,Dataset使用特殊的编码器来序列化对用从而用于处理或网络间传输。尽管这种编码器和标准序列化都会将对象转换为字节,但编码器动态编码并且允许Spark在没有将字节反序列化回对象的时候就执行很多像filtering,sorting,hashing这样的操作。

import java.util.Arrays;
import java.util.Collections;
import java.io.Serializable;

import org.apache.spark.api.java.function.MapFunction;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.Encoder;
import org.apache.spark.sql.Encoders;

public static class Person implements Serializable {
  private String name;
  private int age;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }
}

// Create an instance of a Bean class
Person person = new Person();
person.setName("Andy");
person.setAge(32);

// Encoders are created for Java beans
Encoder<Person> personEncoder = Encoders.bean(Person.class);
Dataset<Person> javaBeanDS = spark.createDataset(
  Collections.singletonList(person),
  personEncoder
);
javaBeanDS.show();
// +---+----+
// |age|name|
// +---+----+
// | 32|Andy|
// +---+----+

// Encoders for most common types are provided in class Encoders
Encoder<Integer> integerEncoder = Encoders.INT();
Dataset<Integer> primitiveDS = spark.createDataset(Arrays.asList(1, 2, 3), integerEncoder);
Dataset<Integer> transformedDS = primitiveDS.map(
    (MapFunction<Integer, Integer>) value -> value + 1,
    integerEncoder);
transformedDS.collect(); // Returns [2, 3, 4]

// DataFrames can be converted to a Dataset by providing a class. Mapping based on name
String path = "examples/src/main/resources/people.json";
Dataset<Person> peopleDS = spark.read().json(path).as(personEncoder);
peopleDS.show();
// +----+-------+
// | age|   name|
// +----+-------+
// |null|Michael|
// |  30|   Andy|
// |  19| Justin|
// +----+-------+

与RDD交互

Spark SQL支持两种不同的方法将现存的RDD转换为Dataset。其一是使用反射机制来推理RDD中具体对象类型的schema。如果编写Spark应用代码时已经知道了schema那这种反射机制就很不错。
其二是通过接口创建schema应用到RDD上来创建Dataset。尽管比第一种冗长,但可以直到运行时才知道列和类型进而构建Dataset。

使用反射机制进行推理schema

Spark SQL会将Row对象组成的RDD转换为DataFrame,并自动推理数据类型。Row对象就是由Row类构造,其参数是一系列键值对。一系列键代表表中的列名,泛型由对整个数据集采样而推理得来,这很像JSON文件上的推理过程。

from pyspark.sql import Row

sc = spark.sparkContext

# Load a text file and convert each line to a Row.
lines = sc.textFile("examples/src/main/resources/people.txt")
parts = lines.map(lambda l: l.split(","))
people = parts.map(lambda p: Row(name=p[0], age=int(p[1])))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值