Spark Sql&DataFrame&RDD&DataSet

SparkSQL

SparkSQL用来处理那些不能够用sql来进行处理的数据逻辑或者用sql处理起来比较复杂的数据逻辑。
使用sparkSQL是为了解决一般用sql不能解决的复杂逻辑,使用编程语言的优势来解决问题。

spark sql流程:

  • 把数据读入到sparkSQL中,sparkSQL进行数据处理或者算法实现,然后再把处理后的数据输出到相应的输出源中。
  • 数据源:hive数据仓库、json文件,.txt,以及orc文件,同时现在还支持jdbc从关系数据库中取数据。
  • 数据类型映射DataSet
  • DataFrame组织数据
  • 数据存入
  • sparkSQL中HiveContext,SQLContext。hiveContext继承了SQLContext的所有方法,同时又对其进行了扩展。hive和mysql的查询还是有一定的差别的。HiveContext只是用来处理从hive数据仓库中读入数据的操作,SQLContext可以处理sparkSQL能够支持的剩下的所有的数据源。这两个类处理的力度是限制在对数据的读写上,同时对表级别的操作上,比如,读入数据、缓存表、释放缓存表、注册表、删除注册的表、返回表的结构等的操作。
sparkSQL的sqlContext的运行原理:
  • 1.SQL语句经过SqlParse解析成UnresolvedLogicalPlan;
  • 2.使用analyzer结合数据数据字典(catalog)进行绑定,生成resolvedLogicalPlan;
  • 3.使用optimizer对resolvedLogicalPlan进行优化,生成optimizedLogicalPlan;
  • 4.使用SparkPlan将LogicalPlan转换成PhysicalPlan;
  • 5.使用prepareForExecution()将PhysicalPlan转换成可执行物理计划;
  • 6.使用execute()执行可执行物理计划;
  • 7.生成SchemaRDD。
  • 在整个运行过程中涉及到多个SparkSQL的组件,如SqlParse、analyzer、optimizer、SparkPlan等等
sparkSQL的hiveContext的运行原理:
  • 1.SQL语句经过HiveQl.parseSql解析成Unresolved LogicalPlan,在这个解析过程中对hiveql语句使用getAst()获取AST树,然后再进行解析;
  • 2.使用analyzer结合数据hive源数据Metastore(新的catalog)进行绑定,生成resolved LogicalPlan;
  • 3.使用optimizer对resolved LogicalPlan进行优化,生成optimized LogicalPlan,优化前使用了ExtractPythonUdfs(catalog.PreInsertionCasts(catalog.CreateTables(analyzed)))进行预处理;
  • 4.使用hivePlanner将LogicalPlan转换成PhysicalPlan;
  • 5.使用prepareForExecution()将PhysicalPlan转换成可执行物理计划;
  • 6.使用execute()执行可执行物理计划;
  • 7.执行后,使用map(_.copy)将结果导入SchemaRDD。

DataFrame与DataSet

spark的核心是RDD,它是弹性分布式数据集,对应着一系列的操作。
Spark SQL是 spark中数据处理的的一个模块,提供了抽象的数据操作方法,可以分布式的查询数据集,叫做DataFrame。
sparkSQL还可以从现有的数据集例如从hive种直接读取数据。
DataFrame是一个分布式以列名为为组织的数据集集合,在本质上也相当于数据库中的一个数据表,与之不同的是他又自己一系列的优化方法共大家使用,数据结构的文件(json格式),hive中的table,外部数据集,现有的RDD都可以产生DataFrame。
spark提供了很大的方便来运行代码,我们可以在spark-shell中来看看具体的应用。
DataFrame与RDD的主要区别就是,前者带有schema元信息,即DataFrame所表示的二维表数据集的每一列都带有名称和类型

数据集和数据框架

数据集是分布式数据集合。数据集是Spark 1.6中添加的一个新接口,它提供了RDD的优势(强类型,使用强大的lambda函数的能力)以及Spark SQL优化执行引擎的优点。数据集可以被构造从JVM对象,然后使用功能性的转换(操作map,flatMap,filter等等)。数据集API在Scala和 Java中可用。Python没有对Dataset API的支持。但由于Python的动态特性,数据集API的许多好处已经可用(即您可以自然地按名称访问行的字段 row.columnName)。R的情况类似。
DataFrame是组织为命名列的数据集。它在概念上等同于关系数据库中的表或R / Python中的数据框,但在引擎盖下具有更丰富的优化。DataFrame可以从多种来源构建,例如:结构化数据文件,Hive中的表,外部数据库或现有RDD。DataFrame API在Scala,Java,Python和R中可用。在Scala和Java中,DataFrame由Rows 的数据集表示。在Scala API中,DataFrame它只是一个类型别名Dataset[Row]。而在Java API中,用户需要使用Dataset来表示DataFrame。
我们经常将Rows的Scala / Java数据集称为DataFrame。

相关概念
  • RDD:spark中最基本的 弹性分布式数据集,提供了很多api 来操作数据集中的元素
  • DataFrame:spark的基于RDD的一种高级抽象,在RDD之上加入了scheme信息,给RDD的元素的每一列提供了 名称和数据类型 的标志;同 时它还提供了更多的api,可以实现类似于sql的操作;但是DataFrame也丢掉了RDD的优点:编译时类型检查和面向对象
  • DataSet:引入了Encoder,成功结合了RDD 和 DataFrame 的优点 (dataset从spark1.6开始引入的)
  • Hive:在hadoop发展过程中,提供给熟悉RDBMS但不了解MapReduce的技术人员的工具,是一种 SQL-ON-HADOOP的工具
  • Shark:也是一种 SQL-ON-HADOOP的工具,依赖于hive,但是效率比hive高
  • Hive on Spark:hive的发展计划,以spark为hive的底层引擎之一
  • Spark sql:脱胎于shark,兼容hive,但是效率更高
  • SqlContext:能从不同的数据源加载数据,将数据转换成DataFrame;也能将DataFrame转换成sqlContext自身中的表,使用sql来操作数据;可以使用.sql()方法,直接查询表中的数据,将返回的数据封装成DataFrame;
  • HiveContext:继承自SQLContext,除了SQLContext中的功能之外,还具有直接操作hive库中表的数据,和HQL兼容
版本依赖:
2.0之前:
  • RDD的创建: 依赖于 sparkcontext;
  • 流处理: 依赖于 streamingcontext
  • sql: 依赖于 sqlcontext
  • hive: 依赖于 hivecontext
2.0之后:
  • 都统一于 sparksession,sparksession 封装了 sparkcontext,sqlcontext
  • sparksession 的创建:val SparkSession = SparkSession.builder.master(“sss”).appName(“sss”).getOrCreate
  • hive的 sparksession 的创建:val SparkSession = SparkSession.builder.master(“sss”).appName(“sss”).enableHiveSupport.getOrCreate
  • 获取sparkcontext:val SparkContext = sparkSession.sparkContext
  • 获取sqlcontext:val SQLContext = sparkSession.sqlContext
反射机制:

简单的来说,反射机制就是先定义一个case class,这个class包含若干个参数,然后利用反射机制来提取参数,就可以得到所对应的参数来当作table的列名。然而case class的作用,注册了table之后,可以进行相应的查询增添修改
具体代码如下:

case class Students(id:Int,name:String,age:Int)

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.types._
import org.apache.spark.sql.{Row, SQLContext}

object StartApp {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("kangjin000").setMaster("spark://db128:7077")
    val sc = new SparkContext(conf)
    val sqlContext = new SQLContext(sc)
    val fileRDD = sc.textFile("hdfs://db128:9000/spark/students.txt")
    val lineRDD = fileRDD.map(line => line.split(","))
    //将RDD和case class关联
    val studentsRDD = lineRDD.map(x => Students(x(0).toInt,x(1),x(2).toInt))
    //在scala中使用反射方式,进行rdd到dataframe的转换,需要手动导入一个隐式转换
    import sqlContext.implicits._
    val studentsDF = studentsRDD.toDF()
    //注册表
    studentsDF.registerTempTable("t_students")
    val df = sqlContext.sql("select * from t_students")
    // SQL查询的结果是DataFrames,支持所有正常的RDD操作。
    df.map(t => "Name: " + t(1)).collect().foreach(println)
    df.map(t => "Name: " + t.getAs[String]("name")).collect().foreach(println)
    //getValuesMap[T]一次检索多个列到映射[String, T]
    df.map(_.getValuesMap[Any](List("id","name", "age"))).collect().foreach(println)
//    df.rdd.foreach(row => println(row(0)+","+row(1)+","+row(2)))
    val count =sqlContext.sql("SELECT * FROM t_students").collect().head.getLong(0)
    println("COUNT : "+count)
  }
}

package com.bigdata.kangjin.context

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.types._
import org.apache.spark.sql.{Row, SQLContext}

object StartApp {
  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("kangjin000")
    val sc = new SparkContext(conf)
    val sqlContext = new SQLContext(sc)
    //指定地址创建rdd
    val studentsRDD = sc.textFile("hdfs://db128:9000/spark/students.txt").map(_.split(","))
    //将rdd映射到rowRDD
    val RowRDD = studentsRDD.map(x => Students(x(0).toInt,x(1),x(2).toInt))
    //以编程方式动态构造元素据val schemaString = "id name age"//字符串
    //import org.apache.spark.sql.types.{StructType,StructField,StringType};   
 val schema = StructType(
      List(
        StructField("id",IntegerType,true),
        StructField("name",StringType,true),
        StructField("age",IntegerType,true)
      )
    )
    //将schema信息映射到rowRDD
    val studentsDF = sqlContext.createDataFrame(RowRDD,schema)
    //注册表
    studentsDF.registerTempTable("t_students")
    val df = sqlContext.sql("select * from t_students order by age")
    df.rdd.collect().foreach(row => println(row))
    df .map(t => "Name: " + t(0)).collect().foreach(println)
  }
}

相关api

1) SparkSession 的创建:
val sparkSession = SparkSession.builder.
master("sss")
.appName("sss")
.config("spark.some.config.option", "some-value")
.getOrCreate
2) 数据源 DataFrame 的创建:
① 从 csv 数据源 创建:
  1、在build.sbt文件里面添加spark-csv支持库:
		SBT工程,加入以下依赖到build.sbt文件中:
		libraryDependencies += "com.databricks" % "spark-csv_2.10" % "2.2.0"
		Maven工程,加入以下依赖到pom.xml文件中:
		 <dependency>
			<groupid>com.databricks</groupid>
			<artifactid>spark-csv_2.10</artifactid>
			<version>1.3.0</version>
		</dependency>
  2、创建SparkConf对象,其中包括Spark运行所有的环境信息:本地的方式运行,使用2个核(local[2]import org.apache.spark.SparkConf
		val conf = new SparkConf().setAppName("csvDataFrame").setMaster("local[2]")
  3、创建SparkContext对象,它是进入Spark的核心切入点:
		val sc = new SparkContext(conf) 
		val sqlContext=new SQLContext(sc) 
  4、使用SQLContext对象加载CSV文件;
		import com.databricks.spark.csv._ 
		val students=sqlContext.csvFile(filePath="StudentData.csv", useHeader=true, delimiter='|') //students对象的类型是org.apache. spark.sql.DataFrame。
		csvFile方法接收需要加载的csv文件路径filePath,如果需要加载的csv文件有头部信息,我们可以将useHeader设置为true,这样就可以将第一行的信息当作列名称来读;delimiter指定csv文件列之间的分隔符。
        除了使用csvFile函数,还可以使用sqlContext里面的load来加载csv文件:
		val options = Map("header" -> "true", "path" -> "E:\\StudentData.csv")
		val newStudents = sqlContext.read.options(options).format("com.databricks.spark.csv").load()
		StudentData.csv文件的部分数据:
		id|studentName|phone|email
		1|Burke|1-300-746-8446|ullamcorper.velit.in@ametnullaDonec.co.uk
		2|Kamal|1-668-571-5046|pede.Suspendisse@interdumenim.edu
		3|Olga|1-956-311-1686|Aenean.eget.metus@dictumcursusNunc.edu
		4|Belle|1-246-894-6340|vitae.aliquet.nec@neque.co.uk
		5|Trevor|1-300-527-4967|dapibus.id@acturpisegestas.net
		6|Laurel|1-691-379-9921|adipiscing@consectetueripsum.edu
		7|Sara|1-608-140-1995|Donec.nibh@enimEtiamimperdiet.edu
		8|Kaseem|1-881-586-2689|cursus.et.magna@euismod.org
		9|Lev|1-916-367-5608|Vivamus.nisi@ipsumdolor.com
		10|Maya|1-271-683-2698|accumsan.convallis@ornarelectusjusto.edu
  5、Spark内置是不支持解析CSV文件的,但是Databricks公司开发了一个类库可以支持解析CSV文件。所以我们需要把这个依赖文件加载到依赖文件中(pom.xml或者是build.sbt)
② 从 json 数据源 创建
Spark SQL可以自动推断JSON数据集的模式并将其加载为Dataset[Row]。可以使用SparkSession.read.json()a Dataset[String]或JSON文件完成此转换。
请注意,作为json文件提供的文件不是典型的JSON文件。每行必须包含一个单独的,自包含的有效JSON对象。有关更多信息,请参阅 JSON Lines文本格式,也称为换行符分隔的JSON。
对于常规多行JSON文件,请将multiLine选项设置为true。
val dataFrame = sparkSession.read.json("some json file")
//在创建数据集时通过导入来支持原始类型(Int,String等)和产品类型(案例类)编码器。
import  spark.implicits._
//路径可以是单个文本文件,也可以是存储文本文件的目录
val path = "examples/src/main/resources/people.json"
val peopleDF = spark.read.json(path)
peopleDF.printSchema()
peopleDF.createOrReplaceTempView("people")
val teenagerNamesDF = spark.sql("SELECT name FROM people WHERE age BETWEEN 13 AND 19")
teenagerNamesDF.show()
//或者,可以为数据集[String] 表示的JSON数据集创建DataFrame,每个字符串存储一个JSON对象
val otherPeopleDataset = spark.createDataset(
  """{"name":"Yin","address":{"city":"Columbus","state":"Ohio"}}""" :: Nil)
val otherPeople = spark.read.json(otherPeopleDataset)
otherPeople.show()
③ 默认数据源 parquet 创建
val dataFrame = sparkSession.read.json("some parquet file")
// DataFrames可以保存为Parquet文件
peopleDF.write.parquet("people.parquet")
val parquetFileDF = spark.read.parquet("people.parquet")
parquetFileDF.createOrReplaceTempView("parquetFile")
val namesDF = spark.sql("SELECT name FROM parquetFile WHERE age BETWEEN 13 AND 19")
namesDF.map(attributes => "Name: " + attributes(0)).show()
架构合并
用户可以从简单模式开始,并根据需要逐渐向模式添加更多列。通过这种方式,用户可能最终得到具有不同但相互兼容的模式的多个Parquet文件。
启用:数据源选项设置mergeSchema到true读取镶木文件时(如下面的实施例),或
将全局SQL选项设置spark.sql.parquet.mergeSchema为true。
import spark.implicits._
val squaresDF = spark.sparkContext.makeRDD(1 to 5).map(i => (i, i * i)).toDF("value", "square")
squaresDF.write.parquet("data/test_table/key=1")
val cubesDF = spark.sparkContext.makeRDD(6 to 10).map(i => (i, i * i * i)).toDF("value", "cube")
cubesDF.write.parquet("data/test_table/key=2")
val mergedDF = spark.read.option("mergeSchema", "true").parquet("data/test_table")
mergedDF.printSchema()
Hive Metastore Parquet表转换
在读取和写入Hive Metastore Parquet表时,Spark SQL将尝试使用自己的Parquet支持而不是Hive SerDe来获得更好的性能。此行为由spark.sql.hive.convertMetastoreParquet配置控制 ,默认情况下处于打开状态。
Hive和Parquet之间存在两个主要区别
Hive不区分大小写,而Parquet则不区分大小写
Hive认为所有列都可以为空,而P​​arquet中的可空性很重要
由于这个原因,在将Hive Metastore Parquet表转换为Spark SQL Parquet表时,我们必须将Hive Metastore模式与Parquet模式进行协调。对帐规则是:
两个模式中具有相同名称的字段必须具有相同的数据类型,而不管是否为空。已协调的字段应具有Parquet端的数据类型,以便遵循可为空性。
协调的模式恰好包含Hive Metastore模式中定义的那些字段。
仅出现在Parquet模式中的任何字段都将放在已协调的模式中。
仅出现在Hive Metastore模式中的任何字段都将在协调模式中添加为可空字段。
元数据刷新
Spark SQL缓存Parquet元数据以获得更好的性能。启用Hive Metastore Parquet表转换后,还会缓存这些转换表的元数据。如果这些表由Hive或其他外部工具更新,则需要手动刷新它们以确保元数据一致。
spark.catalog.refreshTable("my_table")
组态
④ 从 sql 创建
val dataFrame : DataFrame = sparkSession.sql("select ******")
⑤ 从 load 函数(数据源)建
默认数据源(默认是parquet): val dataFrame = sparkSession.read.load("some parquet file")
指定数据源: val dataFrame = sparkSession.read.format("json").json("some json file")
⑥ 从RDD 创建:(Scala case class)
case class Employee(id: Int, name: String)
val conf = new SparkConf().setAppName("colRowDataFrame"). setMaster("local[2]") 
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
//定义一个Employee类的List
val listOfEmployees = List(Employee(1, "iteblog"), Employee(2, "Jason"), Employee(3, "Abhi"))
//将listOfEmployees列表传递给SQLContext类的createDataFrame 函数,然后可以调用DataFrame的printuSchema函数,打印出该DataFrame的模式
val empFrame = sqlContext.createDataFrame(listOfEmployees)
empFrame.printSchema
root
 |-- id: integer (nullable = false)
 |-- name: string (nullable = true)
//如果你对默认反射获取到的模式名称不感兴趣,你可以通过withColumnRenamed函数来指定列名:
val empFrameWithRenamedColumns = sqlContext.createDataFrame(listOfEmployees).withColumnRenamed("id", "empId")
empFrameWithRenamedColumns.printSchema
root
 |-- empId: integer (nullable = false)
 |-- name: string (nullable = true)
//对DataFrame注册成一张临时表	empFrameWithRenamedColumns.registerTempTable("employeeTable")
val sortedByNameEmployees = sqlContext.sql("select * from employeeTable order by name desc")
sortedByNameEmployees.show()
7 ORC文件创建
从Spark 2.3开始,Spark支持带有ORC文件的格式。为此,新添加了以下配置。USING ORC当spark.sql.orc.impl 设置为native和spark.sql.orc.enableVectorizedReader设置为true时。对于Hive ORC serde表,spark.sql.hive.convertMetastoreOrc也设置为true
8 Hive数据源创建
Spark SQL还支持读取和写入存储在Apache Hive中的数据。但是,由于Hive具有大量依赖项,因此这些依赖项不包含在默认的Spark分发中。如果可以在类路径上找到Hive依赖项,Spark将自动加载它们。请注意,这些Hive依赖项也必须存在于所有工作节点上,因为它们需要访问Hive序列化和反序列化库(SerDes)才能访问存储在Hive中的数据。
hive的结构是通过将您做hive-site.xml,core-site.xml(安全性配置),以及hdfs-site.xml(对于HDFS配置)文件中conf/。
在使用Hive时,必须SparkSession使用Hive支持进行实例化,包括连接到持久性Hive Metastore,支持Hive serdes和Hive用户定义函数。没有现有Hive部署的用户仍可以启用Hive支持。如果未配置hive-site.xml,则上下文会自动metastore_db在当前目录中创建并创建由其配置spark.sql.warehouse.dir的目录spark-warehouse,该目录默认为 当前目录中启动Spark应用程序的目录。
注意,自Spark 2.0.0以来,该hive.metastore.warehouse.dir属性hive-site.xml已被弃用。而是spark.sql.warehouse.dir用于指定仓库中数据库的默认位置。你可能需要向启动Spark应用程序的用户授予写入权限。
import java.io.File
import org.apache.spark.sql.{Row, SaveMode, SparkSession}
case class Record(key: Int, value: String)
val warehouseLocation = new File("spark-warehouse").getAbsolutePath

val spark = SparkSession
  .builder()
  .appName("Spark Hive Example")
  .config("spark.sql.warehouse.dir", warehouseLocation)
  .enableHiveSupport()
  .getOrCreate()

import spark.implicits._
import spark.sql

sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING) USING hive")
sql("LOAD DATA LOCAL INPATH 'examples/src/main/resources/kv1.txt' INTO TABLE src")
//hive查询
sql("SELECT * FROM src").show()
//hive聚合
sql("SELECT COUNT(*) FROM src").show()
// SQL查询的结果本身就是DataFrames并支持所有正常的功能。functions.
val sqlDF = sql("SELECT key, value FROM src WHERE key < 10 ORDER BY key")
// DataFrames中的项目为Row类型,允许按顺序访问每列
val stringsDS = sqlDF.map {
  case Row(key: Int, value: String) => s"Key: $key, Value: $value"
}
stringsDS.show()
//还可以使用DataFrames在SparkSession中创建临时视图。
val recordsDF = spark.createDataFrame((1 to 100).map(i => Record(i, s"val_$i")))
recordsDF.createOrReplaceTempView("records")
//然后,查询可以将DataFrame数据与存储在Hive中的数据连接起来。
sql("SELECT * FROM records r JOIN src s ON r.key = s.key").show()
//创建一个Hive托管Parquet表,使用HQL语法而不是Spark SQL本机语法
//“USING hive” 
sql("CREATE TABLE hive_records(key int, value string) STORED AS PARQUET")
//将DataFrame保存到Hive托管表
val df = spark.table("src")
df.write.mode(SaveMode.Overwrite).saveAsTable("hive_records")
sql("SELECT * FROM hive_records").show()
//准备一个目录
val dataDir = "/tmp/parquet_data"
spark.range(10).write.parquet(dataDir)
//创建一个Hive外部Parquet表
sql("CREATE EXTERNAL TABLE hive_ints(key int) STORED AS PARQUET LOCATION '$dataDir'")
sql("SELECT * FROM hive_ints").show()
//打开hive的分区
spark.sqlContext.setConf("hive.exec.dynamic.partition", "true")
spark.sqlContext.setConf("hive.exec.dynamic.partition.mode", "nonstrict")
//分区列`key`将移动到架构的末尾。
df.write.partitionBy("key").format("hive").saveAsTable("hive_part_tbl")
sql("SELECT * FROM hive_part_tbl").show()
spark.stop()
指定Hive表的存储格式
创建Hive表时,需要定义此表应如何从/向文件系统读取/写入数据,即“输入格式”和“输出格式”。您还需要定义此表如何将数据反序列化为行,或将行序列化为数据,即“serde”。以下选项可用于指定存储格式(“serde”,“输入格式”,“输出格式”),例如CREATE TABLE src(id int) USING hive OPTIONS(fileFormat 'parquet')。默认情况下,我们将表文件作为纯文本读取。请注意,创建表时不支持Hive存储处理程序,您可以使用Hive端的存储处理程序创建表,并使用Spark SQL读取它。

与不同版本的Hive Metastore交互
Spark SQL的Hive支持最重要的部分之一是与Hive Metastore的交互,这使得Spark SQL能够访问Hive表的元数据。从Spark 1.4.0开始,可以使用单个二进制构建的Spark SQL来查询不同版本的Hive Metastores,使用下面描述的配置。
注意,独立于用于与Metastore通信的Hive版本,内部Spark SQL将针对Hive 1.2.1进行编译,并使用这些类进行内部执行(serdes,UDF,UDAF等)。
以下选项可用于配置用于检索元数据的Hive版本:
9 JDBC到其他数据库
Spark SQL还包括一个可以使用JDBC从其他数据库读取数据的数据源。与使用JdbcRDD相比,此功能应该更受欢迎。这是因为结果作为DataFrame返回,它们可以在Spark SQL中轻松处理或与其他数据源连接。JDBC数据源也更易于使用Java或Python,因为它不需要用户提供ClassTag。(请注意,这与Spark SQL JDBC服务器不同,后者允许其他应用程序使用Spark SQL运行查询)。
需要在spark类路径中包含特定数据库的JDBC驱动程序。例如,要从Spark Shell连接到postgres
将运行以下命令:
bin/spark-shell --driver-class-path postgresql-9.4.1207.jar --jars postgresql-9.4.1207.jar
可以使用Data Sources API将远程数据库中的表加载为DataFrame或Spark SQL临时视图。用户可以在数据源选项中指定JDBC连接属性。 user并且password通常作为用于登录数据源的连接属性提供。除连接属性外,Spark还支持以下不区分大小写的选项:
物业名称 含义
url 要连接的JDBC URL。可以在URL中指定特定于源的连接属性。例如,jdbc:postgresql://localhost/test?user=fred&password=secret
dbtable 应该读取或写入的JDBC表。请注意,在读取路径中使用它时,可以使用在FROMSQL查询的子句中有效的任何内容。例如,您也可以在括号中使用子查询,而不是完整的表。不允许同时指定`dbtable`和`query`选项。
query 将用于将数据读入Spark的查询。指定的查询将括起来并用作FROM子句中的子查询。Spark还将为子查询子句分配别名。例如,spark将向JDBC Source发出以下形式的查询。SELECT <columns> FROM (<user_specified_query>) spark_gen_alias使用此选项时,以下是一些限制。不允许同时指定`dbtable`和`query`选项。不允许同时指定`query`和`partitionColumn`选项。当需要指定`partitionColumn`选项时,可以使用`dbtable`选项指定子查询,并且可以使用作为`dbtable`的一部分提供的子查询别名来限定分区列。例:spark.read.format("jdbc")&nbsp&nbsp .option("dbtable", "(select c1, c2 from t1) as subq")&nbsp&nbsp .option("partitionColumn", "subq.c1"&nbsp&nbsp .load()
driver 用于连接到此URL的JDBC驱动程序的类名。
partitionColumn, lowerBound, upperBound 如果指定了任何选项,则必须全部指定这些选项。另外, numPartitions必须指定。它们描述了在从多个工作者并行读取时如何对表进行分区。 partitionColumn必须是相关表中的数字,日期或时间戳列。请注意,lowerBound和upperBound只是用来决定分区步幅,而不是在表中过滤行。因此,表中的所有行都将被分区并返回。此选项仅适用于阅读。
numPartitions 表读写中可用于并行的最大分区数。这也确定了最大并发JDBC连接数。如果要写入的分区数超过此限制,我们会coalesce(numPartitions)在写入之前通过调用将其减少到此限制。
queryTimeout 驱动程序等待Statement对象执行到指定秒数的秒数。零意味着没有限制。在写入路径中,此选项取决于JDBC驱动程序如何实现API setQueryTimeout,例如,h2 JDBC驱动程序检查每个查询的超时而不是整个JDBC批处理。它默认为0。
fetchsize JDBC提取大小,用于确定每次往返要获取的行数。这可以帮助JDBC驱动程序的性能,默认为低读取大小(例如,Oracle有10行)。此选项仅适用于阅读。
batchsize JDBC批处理大小,用于确定每次往返要插入的行数。这可以帮助JDBC驱动程序的性能。此选项仅适用于书写。它默认为1000。
isolationLevel 事务隔离级别,适用于当前连接。它可以是一个NONE,READ_COMMITTED,READ_UNCOMMITTED,REPEATABLE_READ,或SERIALIZABLE,对应于由JDBC的连接对象定义,缺省值为标准事务隔离级别READ_UNCOMMITTED。此选项仅适用于书写。请参阅文档java.sql.Connection。
sessionInitStatement 在向远程数据库打开每个数据库会话之后,在开始读取数据之前,此选项将执行自定义SQL语句(或PL / SQL块)。使用它来实现会话初始化代码。例:option("sessionInitStatement", """BEGIN execute immediate 'alter session set "_serial_direct_read"=true'; END;""")
truncate 这是JDBC编写器相关选项。当SaveMode.Overwrite启用时,此选项会导致火花截断,而不是删除和重建其现有的表。这可以更有效,并且防止删除表元数据(例如,索引)。但是,它在某些情况下不起作用,例如当新数据具有不同的模式时。它默认为false。此选项仅适用于书写。
cascadeTruncate 这是JDBC编写器相关选项。如果JDBC数据库(PostgreSQL和Oracle目前)启用并支持,则此选项允许执行a TRUNCATE TABLE t CASCADE(在TRUNCATE TABLE ONLY t CASCADE执行PostgreSQL的情况下执行a以防止无意中截断后代表)。这将影响其他表,因此应谨慎使用。此选项仅适用于书写。它默认为有问题的JDBC数据库的默认级联截断行为,isCascadeTruncate在每个JDBCDialect中指定。
createTableOptions 这是JDBC编写器相关选项。如果指定,则此选项允许在创建表时设置特定于数据库的表和分区选项(例如,CREATE TABLE t (name string) ENGINE=InnoDB.)。此选项仅适用于书写。
createTableColumnTypes 创建表时要使用的数据库列数据类型而不是默认值。数据类型信息应以与CREATE TABLE列语法相同的格式指定(例如:"name CHAR(64), comments VARCHAR(1024)")。指定的类型应该是有效的spark sql数据类型。此选项仅适用于写入。
customSchema 用于从JDBC连接器读取数据的自定义架构。例如,"id DECIMAL(38, 0), name STRING"。您还可以指定部分字段,其他字段使用默认类型映射。例如,"id DECIMAL(38, 0)"。列名应与JDBC表的相应列名相同。用户可以指定Spark SQL的相应数据类型,而不是使用默认值。此选项仅适用于阅读。
pushDownPredicate 用于启用或禁用谓词下推到JDBC数据源的选项。默认值为true,在这种情况下,Spark会尽可能地将过滤器下推到JDBC数据源。否则,如果设置为false,则不会将过滤器下推到JDBC数据源,因此所有过滤器都将由Spark处理。当Spark通过比JDBC数据源更快地执行谓词过滤时,谓词下推通常会被关闭。

//注意:可以通过load / save或jdbc方法实现JDBC加载和保存
//从JDBC源加载数据
val jdbcDF = spark.read
  .format("jdbc")
  .option("url", "jdbc:postgresql:dbserver")
  .option("dbtable", "schema.tablename")
  .option("user", "username")
  .option("password", "password")
  .load()

val connectionProperties = new Properties()
connectionProperties.put("user", "username")
connectionProperties.put("password", "password")
val jdbcDF2 = spark.read.jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

//指定读取模式
connectionProperties.put("customSchema", "id DECIMAL(38, 0), name STRING")

val jdbcDF3 = spark.read
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

//将数据保存到JDBC源
jdbcDF.write
  .format("jdbc")
  .option("url", "jdbc:postgresql:dbserver")
  .option("dbtable", "schema.tablename")
  .option("user", "username")
  .option("password", "password")
  .save()

jdbcDF2.write
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

//指定create table列数据类型
jdbcDF.write.option("createTableColumnTypes", "name CHAR(64), comments VARCHAR(1024)").jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)
10 Avro数据源
该spark-avro模块是外部的,不包括在内spark-submit或spark-shell默认情况下。
与任何Spark应用程序一样,spark-submit用于启动您的应用程序。spark-avro_2.11 并且它的依赖关系可以直接添加到spark-submit使用--packages

将DataFrame保存成文件
  1、创建一个map对象,用于存储一些save函数需要用到的一些属性。这里制定保存文件的存放路径和csv的头信息。
val saveOptions = Map("header" -> "true", "path" -> "iteblog.csv")从DataFrame里面选择出studentName和email两列,并且将studentName的列名重定义为name。
val copyOfStudents = students.select(students("studentName").as("name"), students("email"))
	2、下面调用save函数保存上面的DataFrame数据到iteblog.csv文件夹中copyOfStudents.write.format("com.databricks.spark.csv").mode(SaveMode.Overwrite).options(saveOptions).save()
mode函数可以接收的参数有:
Overwrite代表覆盖目录下之前存在的数据;
Append代表给指定目录下追加数据;
Ignore代表如果目录下已经有文件,那就什么都不执行;
ErrorIfExists代表如果保存目录下存在文件,那么抛出相应的异常。需要注意的是,上述path参数指定的是保存文件夹,并不是最后的保存文件名。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值