一、自动分区推断

1、概述

表分区是一种常见的优化方式,比如Hive中就提供了表分区的特性。在一个分区表中,不同分区的数据通常存储在不同的目录中,
分区列的值通常就包含在了分区目录的目录名中。Spark SQL中的Parquet数据源,支持自动根据目录名推断出分区信息。
例如,如果将人口数据存储在分区表中,并且使用性别和国家作为分区列。那么目录结构可能如下所示: tableName
|- gender=male
|- country=US
...
...
...
|- country=CN
...
|- gender=female
|- country=US
...
|- country=CH
... 如果将/tableName传入SQLContext.read.parquet()或者SQLContext.read.load()方法,那么Spark SQL就会自动根据目录结构,推断出分区信息,是gender和country。
即使数据文件中只包含了两列值,name和age,但是Spark SQL返回的DataFrame,调用printSchema()方法时,会打印出四个列的值:name,age,country,gender。这就是自动分区推断的功能。 此外,分区列的数据类型,也是自动被推断出来的。目前,Spark SQL仅支持自动推断出数字类型和字符串类型。有时,用户也许不希望Spark SQL自动推断分区列的数据类型。
此时只要设置一个配置即可, spark.sql.sources.partitionColumnTypeInference.enabled,默认为true,即自动推断分区列的类型,设置为false,即不会自动推断类型。
禁止自动推断分区列的类型时,所有分区列的类型,就统一默认都是String。 案例:自动推断用户数据的性别和国家

2、java案例实现

##创建hdfs目录,上传文件
##创建了一个users目录,之下又创建了性别=男,国家=US两个目录
[root@spark1 sql]# hdfs dfs -mkdir /spark-study/users
[root@spark1 sql]# hdfs dfs -mkdir /spark-study/users/gender=male
[root@spark1 sql]# hdfs dfs -mkdir /spark-study/users/gender=male/country=US
[root@spark1 sql]# hdfs dfs -put users.parquet /spark-study/users/gender=male/country=US --------------
package cn.spark.study.sql; import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.sql.DataFrame;
import org.apache.spark.sql.SQLContext; public class ParquetPartitionDiscovery {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("ParquetPartitionDiscovery");
JavaSparkContext sc = new JavaSparkContext();
SQLContext sqlConf = new SQLContext(sc); DataFrame usersDF = sqlConf.read().parquet("hdfs://spark1:9000/spark-study/users/gender=male/country=US/users.parquet"); usersDF.printSchema();
usersDF.show(); } } ##打包、上传 ##运行脚本
[root@spark1 sql]# cat ParquetPartitionDiscovery.sh
/usr/local/spark-1.5.1-bin-hadoop2.4/bin/spark-submit \
--class cn.spark.study.sql.ParquetPartitionDiscovery \
--num-executors 3 \
--driver-memory 100m \
--executor-memory 100m \
--executor-cores 3 \
--files /usr/local/hive/conf/hive-site.xml \
--driver-class-path /usr/local/hive/lib/mysql-connector-java-5.1.17.jar \
/usr/local/spark-study/java/sql/saprk-study-java-0.0.1-SNAPSHOT-jar-with-dependencies.jar \ ##结果
##可见,已经自动推断出了性别=男,国家=US两个分区,并加到了字段中
+------+--------------+----------------+------+-------+
| name|favorite_color|favorite_numbers|gender|country|
+------+--------------+----------------+------+-------+
|Alyssa| null| [3, 9, 15, 20]| male| US|
| Ben| red| []| male| US|
+------+--------------+----------------+------+-------+

二、合并元数据

1、概述

如同ProtocolBuffer,Avro,Thrift一样,Parquet也是支持元数据合并的。用户可以在一开始就定义一个简单的元数据,然后随着业务需要,逐渐往元数据中添加更多的列。
在这种情况下,用户可能会创建多个Parquet文件,有着多个不同的但是却互相兼容的元数据。Parquet数据源支持自动推断出这种情况,并且进行多个Parquet文件的元数据的合并。 因为元数据合并是一种相对耗时的操作,而且在大多数情况下不是一种必要的特性,从Spark 1.5.0版本开始,默认是关闭Parquet文件的自动合并元数据的特性的。
可以通过以下两种方式开启Parquet数据源的自动合并元数据的特性:
1、读取Parquet文件时,将数据源的选项,mergeSchema,设置为true
2、使用SQLContext.setConf()方法,将spark.sql.parquet.mergeSchema参数设置为true 案例:合并学生的基本信息,和成绩信息的元数据

2、scala案例实现

package cn.spark.study.sql

import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.SaveMode object ParquetMergeSchema {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("ParquetMergeSchema")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc) import sqlContext.implicits._ // 创建一个DataFrame,作为学生的基本信息,并写入一个parquet文件中
// toSeq转换为 Seq; Seq是列表,适合存有序重复数据,进行快速插入/删除元素等场景
// sc.parallelize: 创建并行集合,2:指定了将数据集切分为2份
val studentWithNameAge = Array(("leo", 30), ("jack", 26)).toSeq
val studentWithNameAgeDF = sc.parallelize(studentWithNameAge, 2).toDF("name", "age")
studentWithNameAgeDF.save("hdfs://spark1:9000/spark-study/students", "parquet", SaveMode.Append) // 创建第二个DataFrame,作为学生的成绩信息,并写入一个parquet文件中
val studentWithNameGrade = Array(("tom", "A"), ("marry", "B")).toSeq
val studentWithNameGradeDF = sc.parallelize(studentWithNameGrade, 2).toDF("name", "grade")
studentWithNameGradeDF.save("hdfs://spark1:9000/spark-study/students", "parquet", SaveMode.Append) // 首先,第一个DataFrame和第二个DataFrame的元数据肯定是不一样的
// 一个是包含了name和age两个列,一个是包含了name和grade两个列
// 所以, 这里期望的是,读取出来的表数据,自动合并两个文件的元数据,出现三个列,name、age、grade // 用mergeSchema的方式,读取students表中的数据,进行元数据的合并
val students = sqlContext.read.option("mergeSchema", "true")
.parquet("hdfs://spark1:9000/spark-study/students") students.printSchema()
students.show() }
} ##打包--上传--运行 ##运行脚本
[root@spark1 sql]# cat ParquetMergeSchema.sh
/usr/local/spark-1.5.1-bin-hadoop2.4/bin/spark-submit \
--class cn.spark.study.sql.ParquetMergeSchema \
--num-executors 3 \
--driver-memory 100m \
--executor-memory 100m \
--executor-cores 3 \
--files /usr/local/hive/conf/hive-site.xml \
--driver-class-path /usr/local/hive/lib/mysql-connector-java-5.1.17.jar \
/usr/local/spark-study/scala/sql/spark-study-scala.jar \ ##结果,两个DataFrame元数据已合并
+-----+----+-----+
| name| age|grade|
+-----+----+-----+
| leo| 30| null|
| jack| 26| null|
|marry|null| B|
| tom|null| A|
+-----+----+-----+

3、java案例实现

package cn.spark.study.sql;

import java.util.ArrayList;
import java.util.List; import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.sql.DataFrame;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.RowFactory;
import org.apache.spark.sql.SQLContext;
import org.apache.spark.sql.SaveMode;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType; public class ParquetMergeSchema {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("ParquetMergeSchemaJava").setMaster("local");
JavaSparkContext sparkContext = new JavaSparkContext(conf);
SQLContext sqlContext = new SQLContext(sparkContext); // 创建一个DataFrame,作为学生的基本信息,并写入一个parquet文件中
List<String> studentWithNameAndAge = new ArrayList<String>();
studentWithNameAndAge.add("tom,18");
studentWithNameAndAge.add("jarry,17");
JavaRDD<String> studentWithNameAndAgeRDD = sparkContext.parallelize(studentWithNameAndAge, 2);
JavaRDD<Row> studentWithNameAndAgeRowRDD = studentWithNameAndAgeRDD
.map(new Function<String, Row>() {
@Override
public Row call(String v1) throws Exception {
return RowFactory.create(v1.split(",")[0], Integer.parseInt(v1.split(",")[1]));
}
}); List<StructField> fieldList = new ArrayList<StructField>();
fieldList.add(DataTypes.createStructField("name", DataTypes.StringType, true));
fieldList.add(DataTypes.createStructField("age", DataTypes.IntegerType, true));
StructType structType = DataTypes.createStructType(fieldList); DataFrame studentWithNameAndAgeDF = sqlContext.createDataFrame(studentWithNameAndAgeRowRDD, structType);
studentWithNameAndAgeDF.write().format("parquet").mode(SaveMode.Append)
.save("hdfs://spark1:9000/spark-study/students"); // 创建第二个DataFrame,作为学生的成绩信息,并写入一个parquet文件中
List<String> studentWithNameAndGrade = new ArrayList<String>();
studentWithNameAndGrade.add("leo,B");
studentWithNameAndGrade.add("jack,A");
JavaRDD<String> studentWithNameAndGradeRDD = sparkContext.parallelize(studentWithNameAndGrade, 2);
JavaRDD<Row> studentWithNameAndGradeRowRDD = studentWithNameAndGradeRDD
.map(new Function<String, Row>() {
@Override
public Row call(String v1) throws Exception {
return RowFactory.create(v1.split(",")[0], v1.split(",")[1]);
}
});
fieldList = new ArrayList<StructField>();
fieldList.add(DataTypes.createStructField("name", DataTypes.StringType, true));
fieldList.add(DataTypes.createStructField("grade", DataTypes.StringType, true));
structType = DataTypes.createStructType(fieldList); DataFrame studentWithNameAndGradeDF = sqlContext.createDataFrame(studentWithNameAndGradeRowRDD, structType);
studentWithNameAndGradeDF.write().format("parquet").mode(SaveMode.Append)
.save("hdfs://spark1:9000/spark-study/students"); // 首先,第一个DataFrame和第二个DataFrame的元数据肯定是不一样的吧
// 一个是包含了name和age两个列,一个是包含了name和grade两个列
// 所以, 这里期望的是,读取出来的表数据,自动合并两个文件的元数据,出现三个列,name、age、grade
// 用mergeSchema的方式,读取students表中的数据,进行元数据的合并
DataFrame df = sqlContext.read().option("mergeSchema", "true")
.parquet("hdfs://spark1:9000/spark-study/students");
df.schema();
df.show();
}
}

39、Parquet数据源之自动分区推断&合并元数据的更多相关文章

  1. spark之数据源之自动分区推断

    在hadoop上创建目录/spark-study/users/gender=male/country=US/users.parquet(并且把文件put上去) code: package cn.spa ...

  2. Mysql 按天自动分区,合并老分区

    适用于每天一个分区...不断加分区,导致分区不够用的情况 CREATE DEFINER=hehe@XXXXXX PROCEDURE p_auto_partition_day(IN databaseNa ...

  3. sparksql parquet 分区推断Partition Discovery

    网上找的大部分资料都很旧,最后翻了下文档只找到了说明 大概意思是1.6之后如果想要使用分区推断就要设置数据源的basePath,因此代码如下 java public class ParitionInf ...

  4. 为已有表快速创建自动分区和Long类型like 的方法-Oracle 11G

    对上一篇文章进行实际的运用.在工作中遇到有一张大表(五千万条数据),在开始的时候忘记了创建自动分区,导致现在使用非常不方便,查询的速度非常的满,所以就准备重新的分区表,最原始方法是先创建新的分区表,然 ...

  5. MySql自动分区

    自动分区需要开启MySql中的事件调度器,可以通过如下命令查看是否开启了调度器 show variables like '%scheduler%'; 如果没开启的话通过如下指令开启 ; 1.创建一个分 ...

  6. C++11新特性:自动类型推断和类型获取

    声明:本文是在Alex Allain的文章http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-functi ...

  7. parquet 合并元数据

    合并元数据:两个数据集,有着一部分相同的列,将他们合并成一个数据集时merge的过程. 合并的规则:相同的列,在新的数据集中,是通用的列, 各自不同的列,也作为新的数据集的列. Spark将数据写入到 ...

  8. Oracle12c:创建主分区、子分区,实现自动分区插入效果

    单表自动单个分区字段使用方式,请参考:<Oracle12c:自动分区表> 两个分区字段时,必须一个主分区字段和一个子分区字段构成(以下代码测试是在oracle12.1版本): create ...

  9. ​ oracle分区表(附带按照月自动分区、按天自动分区)

    --list_range  示例   drop table list_range_tab purge; create table list_range_tab(n1 number,n2 date)pa ...

随机推荐

  1. vim的多文件编辑和多窗口功能

    有的时候我们可能会需要打开多个文件同时进行编辑,例如把一个文件的内容复制到另一个文件中时: 多文件编辑 :n :编辑下一个文件 :N : 编辑上一个文件 :files :列出目前这个vim打开的所有文 ...

  2. C# 生成随机的6位字母,包含大小写

    今天自己做项目需要生成随机的6位字母,于是自己写了一个,下面代码是可以生成任意位数字母的. string _zimu = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg ...

  3. wc命令——Linux系统高效数据统计工具

    wc(world count)是一个统计文件字词,字节,行数的Linux命令,它可以帮我们非常方便的统计以上信息. 主要参数 常见参数如下: -c 统计字节数. -l 统计行数. -m 统计字符数.这 ...

  4. 【转载】C#中List集合使用RemoveRange方法移除指定索引开始的一段元素

    在C#的List集合操作中,移除集合中的元素可以使用Remove方法和RemoveAt方法,这两个方法都是进行单个List集合元素的移除,其实List集合中还有个RemoveRange方法来移除一整段 ...

  5. vue和react之间的区别

    1.Vue和React之间的区别 相同点: Vue和其他框架一样,都有组件开发和虚拟dom 都支持props进行父子组件之间的数据通信 都支持数据驱动视图,不直接操作真实dom 都支持服务器端的 渲染 ...

  6. PHP7预编译mysqli查询操作

    //连接数据库 $mysqli = new mysqli("localhost", "root", "root", "mobile ...

  7. Qt Table Widget常用操作

    一.鼠标悬浮在item上 显示提示信息 1.在构造函数开启table Widget控件的鼠标捕获功能 // 开启鼠标捕获功能(实现table widget的悬浮功能) ui.tableWidget-& ...

  8. 【转】C语言宏定义的几个坑和特殊用法

    总结一下C语言中宏的一些特殊用法和几个容易踩的坑.由于本文主要参考GCC文档,某些细节(如宏参数中的空格是否处理之类)在别的编译器可能有细微差别,请参考相应文档. 宏基础 宏仅仅是在C预处理阶段的一种 ...

  9. shell脚本——正则表达式

    什么是正则表达式 正则表达式分为基础正则和扩展正则,都是为了匹配符合预期要求的字符串 正则表达式与通配符的区别 只需要记住,对文件内容或是展示文本的操作都是正则,而对目录或文件名的操作则都是通配符(例 ...

  10. P1169 [ZJOI2007]棋盘制作[悬线法/二维dp]

    题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8 \times 88×8大小的黑白相间的方阵,对应八八六十四卦,黑白 ...