Spark框架当中,对于Spark SQL而言,进行数据处理的前提是先要引入数据,读取到数据,才能进行接下来的数据处理环节。今天的大数据开发分享,我们就来讲讲Spark SQL数据读取这个环节,也会给到相应的一些示例。
Spark SQL数据读取
1、parquet
1)读取Parquet文件
parquet文件自带schema,读取后是DataFrame格式。
val usersDF =spark.read.load("examples/src/main/resources/users.parquet")
//usersDF: org.apache.spark.sql.DataFrame = [name:string, favorite_color: string ... 1 more field]
2)解析分区信息
parquet文件中如果带有分区信息,那么SparkSQL会自动解析分区信息。比如,这样一份人口数据按照gender和country进行分区存储,目录结构如下:
test
└── spark-sql
└── test
├──gender=male
│ │
│ ├── country=US
│ │ └── data.parquet
│ ├── country=CN
│ │ └── data.parquet
│ └── ...
└──gender=female
│
├── country=US
│ └── data.parquet
├── country=CN
│ └── data.parquet
└── ...
通过spark.read.load读取该目录下的文件SparkSQL将自动解析分区信息,返回的DataFrame的Schema如下:
root
|-- name: string (nullable = true)
|-- age: long (nullable = true)
|-- gender: string (nullable = true)
|-- country: string (nullable = true)
目前自动解析分区支持数值类型和字符串类型。
自动解析分区类型的参数为:spark.sql.sources.partitionColumnTypeInference.enabled,默认值为true。可以关闭该功能,直接将该参数设置为disabled。此时,分区列数据格式将被默认设置为string类型,不会再进行类型解析。
3)Schema合并
如果读取的多个parquet文件中的Schema信息不一致,Spark SQL可以设置参数进行合并,但是Schema合并是一个高消耗的操作,在大多数情况下并不需要,所以Spark SQL从1.5.0开始默认关闭了该功能。
可以通过下面两种方式开启该功能:
a.读取文件的时候,开启合并功能,只对本次读取文件进行合并Schema操作
b.设置全局SQL选项spark.sql.parquet.mergeSchema为true,每次读取文件都会进行合并Schema操作
具体示例:
// sqlContext是之前例子中生成的
// 导入隐式转换
import sqlContext.implicits._
// 创建一个简单的DataFrame并保存
val df1 = sc.makeRDD(1 to 5).map(i => (i, i *2)).toDF("single", "double")
df1.write.parquet("data/test_table/key=1")
// 创建另一个DataFrame,注意字段名
val df2 = sc.makeRDD(6 to 10).map(i => (i, i *3)).toDF("single", "triple")
df2.write.parquet("data/test_table/key=2")
// 读取这两个parquet文件,增加开启合并Schema的设置
val df3 =sqlContext.read.option("mergeSchema","true").parquet("data/test_table")
df3.printSchema()
// 不同名称的字段都保留下来了
// root
// |-- single: int (nullable = true)
// |-- double: int (nullable = true)
// |-- triple: int (nullable = true)
// |-- key : int (nullable = true)
关于schema合并,有一点需要特别关注,那就是当不同parquet文件的schema有冲突时,合并会失败,如同名的字段,其类型不一致的情况。这时如果你读取的是hive数据源,可能会出现读取失败或者读取字段值全部为NULL的情况。如果大家遇到类型场景,可以考虑是否是这个因素导致。
2、json
json文件和parquet文件一样也是带有schema信息,不过需要指明是json文件,才能准确的读取。
val peopleDF =spark.read.format("json").load("examples/src/main/resources/people.json")
//peopleDF: org.apache.spark.sql.DataFrame = [age:bigint, name: string]
3、MySQL
读取MySQL中的数据是通过jdbc的方式,需要知道要访问的MySQL数据库、表等信息,具体请看下面的代码:
//MySQL数据的访问ip、端口号和数据库名
val url ="jdbc:mysql://192.168.100.101:3306/testdb"
//要访问的表名
val table = "test"
//建立一个配置变量
val properties = new Properties()
//将用户名存入配置变量
properties.setProperty("user","root")
//将密码存入配置变量
properties.setProperty("password","root")
//需要传入Mysql的URL、表名、配置变量
val df = sqlContext.read.jdbc(url,table,properties)
这里要注意的一个点是,读取MySQL需要运行作业时,classpath下有MySQL的驱动jar,或者通过--jars添加驱动jar。
4、hive
读取hive数据的前提是要进行相关的配置,需要将hive-site.xml、core-site.xml、hdfs-site.xml以及hive的lib依赖放入spark的classpath下,或者在提交作业时通过--files和--jars来指定这些配置文件和jar包。之后,就可以很方便的使用hive的数据表了,示例代码如下:
import java.io.File
import org.apache.spark.sql.Row
import org.apache.spark.sql.SparkSession
case class Record(key: Int, value: String)
// 数仓地址指向默认设置
val warehouseLocation = newFile("spark-warehouse").getAbsolutePath
val spark = SparkSession
.builder()
.appName("Spark Hive Example")
.config("spark.sql.warehouse.dir", warehouseLocation)
.enableHiveSupport() //增加支持hive特性
.getOrCreate()
import spark.implicits._
import spark.sql
//使用sql创建一个表,并将hdfs中的文件导入到表中
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")
// 使用sql直接指向sql查询
sql("SELECT * FROM src").show()
// +---+-------+
// |key| value|
// +---+-------+
// |238|val_238|
// | 86| val_86|
// |311|val_311|
// ...
关于大数据开发,Spark SQL数据读取,以上就为大家做了简单的介绍了。Spark SQL数据读取,往往需要看引入数据源的格式问题,根据不同的格式选择相应的方式去处理就行了。