对Parquet文件的支持
可以使用Greenplum数据库的gphdfs
协议来访问一个Hadoop文件系统(HDFS)上的Parquet文件。
上级主题: 使用Hadoop分布式文件系统(HDFS)表
关于Parquet文件格式
Parquet文件格式被设计为利用Hadoop生态系统的项目中压缩的、高效的列式数据表示。Parquet支持复杂嵌套数据结构并且使用Dremel的record shredding and assembly算法。Parquet支持非常高效的压缩和编码方式。Parquet允许在列级别上指定压缩方式,并且支持增加更多自定义的编码方式。
有关Parquet的信息,请见Parquet的文档http://parquet.apache.org/documentation/latest/。
列式数据存储和Parquet文件格式的概述可见https://blog.twitter.com/2013/dremel-made-simple-with-parquet。
必需的Parquet Jar文件
对Parquet文件格式的支持要求这些jar文件:* parquet-hadoop-1.7.0.jar
- parquet-common-1.7.0.jar
- parquet-encoding-1.7.0.jar
- parquet-column-1.7.0.jar
- parquet-generator-1.7.0.jar
- parquet-format-2.3.0-incubating.jar
注意:Cloudera 5.4.x Hadoop包括了一些Parquet的jar文件。不过,这些jar文件中的Java类名是parquet.xxx
。gphdfs
协议使用Java类名org.apache.parquet.xxx
。可以下载使用org.apache.parquet
这种类名的jar文件并且将其安装在Greenplum数据库主机上。
gphdfs
协议还支持使用parquet-hadoop-bundle-1.7.0.jar
,它含有在Hadoop环境中使用Parquet所需的类。不支持这些版本的parquet-hadoop-bundle
:* 版本 1.6 及更早的版本。这些版本用的不是org.apache.parquet
这样的Java类名。
- 版本 1.8 及其后的版本。这些版本包含
gphdfs
不支持的类VersionParser
。
关于下载Parquet的jar文件的信息,请见http://parquet.apache.org/downloads/
在Greenplum数据库的所有主机上,确保安装了这些jar文件并且它们都在gphdfs
协议所使用的classpath
上。classpath
由shell脚本$GPHOME/lib/hadoop/hadoop_env.sh
指定。作为一个Hadoop 2的例子,用户可以把那些jar文件安装在$HADOOP_HOME/share/hadoop/common/lib
中。hadoop_env.sh
脚本文件会把那些jar文件加入到classpath
。
例如,如果目录$HADOOP_HOME/share/hadoop/common/lib
不存在,作为gpadmin
用户在Greenplum数据库的所有主机上创建该目录。然后在所有主机上把这些jar文件加到该目录中。
hadoop_env.sh
脚本文件会为gphdfs
协议把那些jar文件增加到classpath
。该脚本文件中是这个片段负责这项工作:
if [ -d "${ HADOOP_HOME}/share/hadoop/common/lib" ]; then
for f in ${ HADOOP_HOME}/share/hadoop/common/lib/*.jar; do
CLASSPATH=${ CLASSPATH}:$f;
done
Parquet文件格式支持
Greenplum数据库的gphdfs
协议支持版本1或2的Parquet文件格式。Parquet利用了HDFS上压缩的列式数据表示。在一个Parquet文件中,包含数据结构信息的元数据(Parquet模式定义)被写在数据之后以允许单趟写。
这是一个Parquet模式定义格式的例子:
message test {
repeated byte_array binary_field;
required int32 int32_field;
optional int64 int64_field;
required boolean boolean_field;
required fixed_len_byte_array(3) flba_field;
required byte_array someDay (utf8);
};
The definition for last field
最后一个域someDay
的定义指定了带有utf8
标注的binary
数据类型。该数据类型和标注定义该数据是一种UTF-8编码的字符串。
读写Parquet文件
要读写一个Parquet文件,需要创建一个外部表并且在LOCATION
子句和FORMAT
子句中分别指定该Parquet文件的位置和'PARQUET'
。例如,这是一个可读外部表的语法。
CREATE EXTERNAL TABLE tablename (column_spec) LOCATION ( 'gphdfs://location') FORMAT 'PARQUET'
location可以是一个Parquet文件或者含有一组Parquet文件的一个目录。对于文件名可以使用通配符字符 * 来匹配任意多个字符。如果在读取Parquet文件时该位置指定的是多个文件,Greenplum数据库会使用第一个文件中的模式作为读取其他文件的模式。
读取一个Parquet文件
下面的表格列出了Greenplum如何在Parquet模式定义没有包含标注时转换Parquet数据类型。
表 1. 读取一个Parquet文件时的数据类型转换
Parquet数据类型 | Greenplum数据库数据类型 |
---|---|
boolean | boolean |
int32 | int 或 smallint |
int64 | long |
int96 | bytea |
float | real |
double | double |
fixed_lenth_byte_array | bytea |
byte_array | bytea |
注意: 在把Parquet的int
数据类型读取为Greenplum数据库的smallint
数据类型时,必须确保Parquet的int
值不会超过Greenplum数据库的最大smallint
值。如果Parquet值太大,Greenplum数据库的值将不准确。
gphdfs
协议为这些情况考虑Parquet的模式标注。对其他情况,数据转换基于Parquet模式的简单类型进行:
表 2. 读取Parquet文件时的数据类型(带标注)转换
Parquet模式的数据类型和标注 | Greenplum数据库数据类型 |
---|---|
带json 或者utf8 标注的binary |
text |
binary和数据类型为text的Greenplum数据库列 | text |
带int_16 标注的int32 |
smallint |
int32、int64、fixed_lenth_byte_array或带decimal 标注的binary |
decimal |
repeated |
array列 - 该数据类型根据表 1转换 |
optional 、required |
数据类型根据表 1转换 |
注意: 在指定decimal
、date
、interval
或者 time*
标注时,请参考限制和注解以及Parquet文档。如果Parquet域类型是不带任何标注的binary,gphdfs
协议把该域的数据转换成文本,并且对应的Greenplum数据库外部表列的数据类型被定义为text。
在读取Parquet的类型group
时,gphdfs
协议把group
数据转换成一个XML文档。
这个模式包含一个名称为inner
的required group。
message test {
required byte_array binary_field;
required int64 int64_field;
required group inner {
int32 age;
required boolean test;
required byte_array name (UTF8);
}
};
这里是group数据的一个单一行怎样被转换成XML的。
<inner type="group">
<age type="int">50</age>
<test type="boolean">true</test>
<name type="string">fred</name>
</inner>
这个模式的例子包含一个名称为inner
的repeated group。
message test {
required byte_array binary_field;
required int64 int64_field;
repeated group inner {
int32 age;
required boolean test;
required byte_array name (UTF8);
}
};
对于一个repeated group
,Parquet文件可以在一个行中包含多组group数据。对于这个模式的例子,inner
group的数据被转换成XML数据。
这是Parquet文件中的数据包含两组inner
group数据时的输出实例。
<inner type="repeated">
<inner type="group">
<age type="int">50</age>
<test type="boolean">true</test>
<name type="string">fred</name>
</inner>
<inner>
<age type="int">23</age>
<test type="boolean">false</test>
<name type="string">sam</name>
</inner>
</inner>
读取一个Hive生成的Parquet文件
Apache Hive数据仓库软件可以管理和查询位于分布式存储中的大型数据集。有关Apache Hive所使用的Parquet的信息请见https://cwiki.apache.org/confluence/display/Hive/Parquet。
对于存储在Parquet文件中的Hive 1.1数据,这个表格列出了Greenplum数据库如何转换该数据。该转换基于Hive生成的Parquet模式进行。有关Hive生成的Parquet模式的信息请见对Hive生成的Parquet模式的注解。
表 3. 读取一个Hive生成的Parquet文件时的数据类型转换
Hive数据类型 | Greenplum数据库数据类型 |
---|---|
tinyint | int |
smallint | int |
int | int |
bigint | bigint |
decimal | numeric |
float | real |
double | float |
boolean | boolean |
string | text |
char | text 或 char |
varchar | text 或 varchar |
timestamp | bytea |
binary | bytea |
array | xml |
map | xml |
struct | xml |
对Hive生成的Parquet模式的注解
- 在对Parquet文件写入数据时,Hive把所有的整数数据类型
tinyint
、smallint
、int
都当作int32
。当在Greenplum数据库中为一个Hive生成的Parquet文件创建一个外部表时,指定这类列数据类型为int
。例如,这个Hive的CREATE TABLE
命令把数据存储在Parquet文件中。
CREATE TABLE hpSimple(c1 tinyint, c2 smallint, c3 int, c4 bigint,
c5 float, c6 double, c7 boolean, c8 string)
STORED AS PARQUET;
这是hpSimple
表数据的Hive生成的Parquet模式。
message hive_schema {
optional int32 c1;
optional int32 c2;
optional int32 c3;
optional int64 c4;
optional float c5;
optional double c6;
optional boolean c7;
optional binary c8 (UTF8);
}
gphdfs
协议把Parquet的整数数据类型转换成Greenplum数据库的数据类型int
。
- 对于Hive的
char
数据类型,Greenplum数据库的列数据类型可以是text
或者char
。对于Hive的varchar
数据类型,Greenplum数据库的列数据类型可以是text
或者varchar
。 - 基于Hive生成的Parquet模式,一些Hive数据被转换成Greenplum数据库的XML数据。例如,存储在一个Parquet文件中的Hive数组列数据会被转换成XML数据。例如,这是一个类型为
array[int]
的Hive列的Hive生成的Parquet模式。optional group col1 (LIST) { repeated group bag { optional int32 array_element; } }
gphdfs
协议把Parquet的group
数据转换成Greenplum数据库的数据类型XML
。
- 对于Hive的
timestamp
数据类型,该数据类型的Hive生成的Parquet模式指定数据被作为数据类型int96
存储。gphdfs
协议把int96
数据类型转换为Greenplum数据库的bytea
数据类型。
写一个Parquet文件
对于可写的外部表,用户可以在location中指定的文件后面加上参数。可以用以?
开始并且域/值对由&
分隔的http查询字符串语法增加参数。
表 4. Parquet格式外部表的位置参数
选项 | 值 | 可读/可写 | 默认值 |
---|---|---|---|
schema | URL_to_schema | 只写 | 无。如果没有指定,gphdfs 协议根据外部表定义创建一个模式。 |
pagesize | > 1024字节 | 只写 | 1 MB |
rowgroupsize | > 1024字节 | 只写 | 8 MB |
version | v1 , v2 |
只写 | v1 |
codec | UNCOMPRESSED 、GZIP 、LZO 、snappy |
只写 | UNCOMPRESSED |
dictionaryenable1 | true 、false |
只写 | false |
dictionarypagesize1 | > 1024字节 | 只写 | 512 KB |
注意:
- 创建一个内部字典。如果文本列包含相似或者重复数据,启用一个字典能够提升Parquet文件的压缩。
在写一个Parquet文件时,gphdfs
协议可以基于表定义生成一个Parquet模式。* 表名被用作Parquet的message
名称。
- 列名被用作Parquet的
域
名称。
在从一个Greenplum数据库表定义创建Parquet模式时,模式基于列的数据类型生成。
表 5. 写一个Parquet文件时的数据类型转换
Greenplum数据库数据类型 | Parquet模式的数据类型 |
---|---|
boolean | optional boolean |
smallint | 带标注int_16 的optional int32 |
int | optional int32 |
bigint | optional int64 |
real | optional float |
double | optional double |
numeric 或 decimal | 带标注decimal 的binary |
bytea | optional binary |
array 列 | repeated 域 - 数据类型是和Greenplum数据库数组相同的数据类型。例如,array[int] 会被转换成repeated int |
其他 | 带标注utf8 的binary |
注意: 要支持Null
数据,在创建一个Parquet模式时用gphdfs
协议指定Parquet的optional
模式标注。
一个Greenplum数据库表定义和gphdfs
协议生成的Parquet模式的简单例子。
一个Parquet文件的外部表定义例子。
CREATE WRITABLE EXTERNAL TABLE films (
code char(5),
title varchar(40),
id integer,
date_prod date,
subtitle boolean
) LOCATION ( 'gphdfs://my-films') FORMAT 'PARQUET' ;
这是gphdfs
协议为Parquet文件my-films
生成的Parquet模式。
message films {
optional byte_array code;
optional byte_array title (utf8);
optional int32 id;
optional binary date_prod (utf8);
optional boolean subtitle;
};
限制和注解
- 对于可写的外部表,Greenplum数据库外部表的列定义不能指定
NOT NULL
来支持自动生成一个Parquet模式。当gphdfs
协议自动生成一个Parquet模式时,gphdfs
协议会指定域属性optional
来支持Parquet模式中的null
。在Parquet中repeated域可以是null
。 gphdfs
协议只为可读的外部表支持Parquet的嵌套group
结构。嵌套结构会被转换成一个XML文档。- Greenplum数据库没有一种无符号的
int
数据类型。Greenplum数据库会把Parquet的无符号int
数据类型转换为下一种最大的Greenplum数据库int
类型。例如,Parquet的uint_8
会被转换成Greenplum数据库的int
(32位)。 - Greenplum数据库支持任何UDT数据类型或者UDT数组数据类型。Greenplum会尝试把UDT转换成一个字符串。如果该UDT不能被转换为一个字符串,Greenplum数据库会返回一个错误。
- Parquet中
Interval
数据类型的定义和Greenplum数据库中的Interval
定义有显著的不同,两者不能被转换。 Parquet的Interval
数据会被格式化为bytea
。 - Parquet中的
Date
数据类型从1970.1.1开始,而Greenplum数据库中的Date
从4173 BC开始,由于最大值不同所以Greenplum数据库不能转换date
数据类型。同样的情况发生在Parquet中的Timestamp_millis
和Greenplum数据库中的Timestamp
之间。