1) 外部数据源产生的背景
每一个spark的app开始于加载数据结束于保存数据,那这个数据保存到哪里?可以是MySQL可以是Hive等等一些数据存储的地方。
用户想要的是方便快速从不同的数据源(json、parquet、rdbms),经过混合处理(json join parquet),再将处理结果以特定的格式(json、parquet)写回到指定的系统(HDFS、S3)上面去。
基于这样的需求spark引入了外部数据源:
Spark SQL 1.2 ==> 外部数据源API
通俗来说外部数据源就是在外部,可能在各个地方,存储在任何地方,你不用关注他在哪,各个地方以什么格式存储的、通过这个API就能够非常快速=方便的进行操作就ok。
如下图所示
1、一种扩展的方式,这种扩展的方式可以集成各种外部数据源,把这些外部数据源集成到SparkSQL里面来,既然已经集成到Spark SQL里面,那么就可以使用DataFrame的API或者SQL的API进行操作,这就很方便了。
2、它能够读和写DataFrame使用各种各样的format格式和存储系统,这怎么理解呢?就是说你可以load进来各种存储系统中的数据,这些数据可以是不同的格式,然后把这些数据读取成一个DataFrame,那么同样的也可以把DataFrame指定格式指定输出路径,写到外面去也是可以的。
3、外部数据源的API可以自动做一些列的裁剪,什么叫列的裁剪,你如一个表厘有A B C D E五个列,在做select的时候你只需要A B C这几个列,那么D E这两个列就相当于被裁减了,还可以把一些条件下压到数据源端去。
4、外部数据源是在1.2版本才提出来的
再看下面的一张图
上面的图中 CSV在spark 2.x 之后 内置到 spark 中
有了Data Source API以后,你下面各个地方的各式各样的格式的数据都能够非常方便的读取和操作了,标准的写法就是xxx.read.dormat("json/txt/csv...........").load(path)
No. 3 目标
1、对于开发人员来说我们只需要构建一个library,构建库,这个库是专门用来对于外部的数据源的,比如说你现在想通过Spark来访问Hbase,访问微博的一些数据,那么你必须开发相应的实现类才行,这是对于开发人员来说的
2、对于使用人员来说就非常容易就可以加载或者保存为DataFrame了,一般用法就是:
读:spark.read.format(format)
format:
build-in: json parquet jdbc csv(2.x)
packages: 外部的, 并不是spark内置的 这有一个网站 :Spark Packages
写:spark.write.format("parquet").save(path)
load data format :json/parquet/txtfile/.. hdfs/s3 等
save data format :json/parquet/txtfile/.. hdfs/s3 等
spark SQL 能够操作各种不同的数据源:HDFS/hive/phoenix/hbase ...
sqlContext.read.format("json/parquet/jdbc/..csv/..avro/..phoenix").load("file/hdfs/s3")
DF.write.format("json/parquet/jdbc/..csv/..avro/..phoenix").mode("overwrite/append/ignore").save("file/hdfs/s3")
read:DataFrameReader
write:DataFrameWriter
中间可能会涉及到一个非常重要的过程 操作
textFile ==> rc/orc:temp ===> insert overwrite into target
val logDF= sqlContext.read.format("json").load('path')
logDF.write.format("parquet").save("path")
在 spark SQL 中默认的数据源格式是parquet
spark-shell
sqlContext.read.format("text").load("/home/hadoop/data/emp.txt").show
sqlContext.read.format("parquet").load("/home/hadoop/data/users.parquet").show
sqlContext.read.format("parquet").option("path","file:///home/hadoop/data/users.parquet").load().show
storage format convert
val df= sqlContext.read.format("parquet").load("/home/hadoop/data/users.parquet")
df.select("name","favorite_color").write.format("json").save("/home/hadoop/data/tmp/")
df.select("name","favorite_color").write.format("json").save("/home/hadoop/data/tmp/")
生产中遇到这样的文件 该怎么办?
上面的spark 1.6.x load 和save 数据的方式
1) spark 1.6.x 读取 JSON 格式的数据
package demo.sparksql
import org.apache.spark.{SparkConf, SparkContext}
/**
* Created by joy on 2017/8/25.
*/
object ReadJson {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setMaster("local[2]")
.setAppName("ReadJson")
val sc = new SparkContext(conf)
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
// 第一种方式
//val path = "file:///192.168.137.251:8020/data/people.json"
//val people = sqlContext.read.json(path)
//第二种方式 读取HDFS上json 文件
//val people = sqlContext.read.json("hdfs://192.168.137.251:8020/data/people.json")
// 读取windos本地的JSON 文件
val people = sqlContext.read.json("data/people.json")
//val people = sqlContext.read.json("file:E:\app\spark-2.2.0-bin-2.6.0-cdh5.7.0\examples\src\main\resources\people.json")
people.show()
people.printSchema()
people.registerTempTable("people")
val teenagers = sqlContext.sql("SELECT name FROM people ")
val teenagers1 = sqlContext.sql("SELECT name FROM people WHERE age >= 13 AND age <= 19")
teenagers.show()
teenagers1.show()
sc.stop()
}
}
2) 读取 parquet 格式的数据文件
ackage demo.sparksql
import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.SaveMode
import org.apache.spark.{SparkConf, SparkContext}
/**
* Created by joy on 2017/8/25.
*/
object ReadParquet {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName(" ReadParquet").setMaster("local[2]")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
// 读取 HDFS上的parquet 格式的文件
//val parquet = sqlContext.read.format("parquet").load("hdfs://192.168.137.251:8020/data/users.parquet")
val parquet = sqlContext.read.format("parquet").load("data/users.parquet")
parquet.registerTempTable("parquet_table")
parquet.show()
val t1 = sqlContext.sql("select * from parquet_table where name = 'red'")
val t2 = sqlContext.sql("select * from parquet_table where favorite_color is null")
sqlContext.read.format("parquet").option("path","").load()
t1.show()
t2.show()
//
sc.stop()
}
}
3) 读取操作 MySQL的数据源
package com.wjl7813
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SQLContext
import java.util.Properties
/**
* Created by 92421 on 2018/3/2.
*/
object OperationMysql {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("OperationMysql").setMaster("local[2]")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
import sqlContext.implicits._
val props = new Properties()
props.put("driverr","com.mysql.jdbc.Driver")
val url = "jdbc:mysql://192.168.137.251:3306/tag_dbuser=root&password=oracle"
val table1 = "users"
val table2 = "tags"
val jdbcDF1 = sqlContext.read.format("jdbc").jdbc(url,table1,props).show()
val jdbcDF2 = sqlContext.read.format("jdbc").jdbc(url,table2,props).show()
sc.stop()
}
}
(2) 方式二
package com.wjl7813
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SQLContext
/**
* Created by 92421 on 2018/3/2.
*/
object ReadMysql {
def main(args: Array[String]): Unit = {
// spark 上下文
val conf = new SparkConf().setAppName("ReadMySQL").setMaster("local[2]")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
import sqlContext.implicits._
// mysql 操作
val jdbcDF1 = sqlContext.read.format("jdbc").options(Map(
"url" -> "jdbc:mysql://192.168.137.251:3306/tag_db",
"driver" -> "com.mysql.jdbc.Driver",
"dbtable" -> "tags",
"user" -> "root",
"password" -> "oracle")).
load()
val jdbcDF2 = sqlContext.read.format("jdbc").options(Map(
"url" -> "jdbc:mysql://192.168.137.251:3306/tag_db",
"driver" -> "com.mysql.jdbc.Driver",
"dbtable" -> "users",
"user" -> "root",
"password" -> "oracle")).
load()
jdbcDF1.registerTempTable("tags")
jdbcDF2.registerTempTable("users")
sqlContext.sql("select * from tags t join users u on t.id = u.id ").show()
sc.stop()
}
}
以上2种方式中,第一种方式可以得到一张表的显示,但是df直接操作譬如select *from talbename 怎么办呢, 推荐 2种方式
=== spark 1.6.x 操作hive表数据=
scala> sqlContext.sql("select deptno ,count(1) from emp group by deptno").show
+------+---+
|deptno|_c1|
+------+---+
| null| 1|
| 10| 3|
| 20| 5|
| 30| 6|
+------+---+
scala> sqlContext.sql("select deptno ,count(1) from emp group by deptno").filter("deptno is not null").show
+------+---+
|deptno|_c1|
+------+---+
| 10| 3|
| 20| 5|
| 30| 6|
+------+---+
scala> sqlContext.sql("select deptno ,count(1) from emp group by deptno").filter("deptno is not null").write.saveAsTable("hive_table")
[Stage 9:> (0 + 2) / 200]SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
18/03/08 23:10:59 WARN HiveMetaStore: Location: hdfs://node1.oracle.com:8020/user/hive/warehouse/hive_table specified for non-external table:hive_table
这条语句sqlContext.sql("select deptno ,count(1) from emp group by deptno").filter("deptno is not null").write.saveAsTable("hive_table") 在 spark 2.x 就会报错
需要修改为 如下的方式
scala> spark.sql("select deptno ,count(1) as mount from emp group by deptno").filter("deptno is not null").write.saveAsTable("hive_table")
scala> sqlContext.sql("select * from hive_table").show
+------+---+
|deptno|_c1|
+------+---+
| 10| 3|
| 20| 5|
| 30| 6|
+------+---+