基本数据类型
和 集合数据类型
4
种带符号的整数类型:TINYINT
、SMALINT
、INT
、BIGINT
,它们分别对应 Java 中的 byte
、short
、int
、long
Hive 整数类型 | Java 整数类型 | 字节长度 | 后缀符 | 例子 |
---|---|---|---|---|
TINYINT | byte | 1 byte 有符号整数 | Y | 100Y |
SMALINT | short | 2 byte 有符号整数 | S | 100S |
INT | int | 4 byte 有符号整数 | — | — |
BIGINT | long | 8 byte 有符号整数 | L | 100L |
默认情况下为 INT
,如果要声明为其他类型,通过后缀符
FLOAT
和 DOUBLE
两种,分别对应 Java 中的 float
和 double
Hive 小数类型 | Java 小数类型 | 字节长度 | 后缀符 | 例子 |
---|---|---|---|---|
FLOAT | float | 4 byte 有符号整数 | F | — |
DOUBLE | double | 8 byte 有符号整数 | D | — |
DECIMAL | BigDecimal | 表示任意精度的小数,通常在货币当中使用 | — | DECIMAL(5,2) |
Hive 中,主要有 3
种类型用于存储文本。
STRING2GB
,但是存储特别大的对象时效率可能受到影响,可以考虑使用 Sqoop 提供的大对象
支持。
VARCHAR 与 STRING 类似,但是长度上只允许在 1-65355 之间。
CHAR 则用固定长度来存储数据。
Hive 文本类型 | Java 文本类型 | 描述 | 例子 |
---|---|---|---|
STRING | String | String 类型可以用单引号(’)或双引号(”)定义。 | 'Hive 教程' |
VARCHAR | — | varchar 类型由长度定义,范围为 1-65355 ,如果存入的字符串长度超过了定义的长度,超出部分会被截断。尾部的空格也会作为字符串的一部分,影响字符串的比较。 | 'Hive 教程' |
CHAR | — | char 是固定长度的,最大长度 255,而且尾部的空格不影响字符串的比较。 | 'Hive 教程' |
:表示二元值的 true
或 false
主要用来存储纳秒级别的时间戳
cast(date as date)
cast(timestamp as date)
cast(string as date)
cast(date as string)
to_utc_timestamp
和 from_utc_timestamp
函数可以用于时区转换。
DATE 类型则表示日期,对应年月日
三个部分。
ARRAY
,MAP
, STRUCT
是一组具有相同数据类型元素的集合,ARRAY 中的元素是有序的,每个元素都有一个编号
,编号从零
# 创建表结构:person_array
CREATE TABLE IF NOT EXISTS person_array (id int,name array<STRING>)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
COLLECTION ITEMS TERMINATED BY ':'
STORED AS TEXTFILE;
# 创建原始数据:person_array.txt
1,'aa':'jp'
2,'bb':'cn'
3,'cc':'jp'
4,'dd':'cn'
5,'ee':'jp'
# 从 HDFS 上 /data 目录下导入数据
hive (default)> LOAD DATA INPATH '/data/person_array.txt' OVERWRITE INTO TABLE person_array;
Loading data to table default.person_array
Table default.person_array stats: [numFiles=1, numRows=0, totalSize=60, rawDataSize=0]
OK
Time taken: 0.434 seconds
# 查询所有数据
hive (default)> select * from person_array;
OK
person_array.id person_array.name
1 ["'aa'","'jp'"]
2 ["'bb'","'cn'"]
3 ["'cc'","'jp'"]
4 ["'dd'","'cn'"]
5 ["'ee'","'jp'"]
Time taken: 0.188 seconds, Fetched: 5 row(s)
# 指定查询 name:array 中的字段信息
hive (default)> select id,name[0],name[1] from person_array where name[1]="'jp'";
OK
id _c1 _c2
1 'aa' 'jp'
3 'cc' 'jp'
5 'ee' 'jp'
是一种键值对
形式的集合,通过 key(键)来快速检索 value(值)。在 Map 中,key 是唯一的
,但 value 可以重复
# 创建表结构:person_map
CREATE TABLE IF NOT EXISTS person_map (id int,name map<STRING,STRING>)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
COLLECTION ITEMS TERMINATED BY ':'
MAP KEYS TERMINATED BY ':'
STORED AS TEXTFILE;
类似于 C、C# 语言,Hive 中定义的 struct 类型也可以使用点
# 创建表结构:person_struct
hive (default)> CREATE TABLE IF NOT EXISTS person_struct (id int,info struct<name:string,country:string>)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
COLLECTION ITEMS TERMINATED BY ':' STORED AS TEXTFILE;
# 创建原始数据:person_struct.txt
1,'aa':'jp'
2,'bb':'cn'
3,'cc':'jp'
4,'dd':'cn'
5,'ee':'jp'
# 从 HDFS 上 /data 目录下导入数据
hive (default)> LOAD DATA INPATH '/data/person_struct.txt' INTO TABLE person_info;
Loading data to table default.person_info
Table default.person_info stats: [numFiles=1, numRows=0, totalSize=72, rawDataSize=0]
OK
# 查询所有数据
hive (default)> select * from person_struct;
OK
person_struct.id person_struct.info
1 {"name":"'aa'","country":"'jp'"}
2 {"name":"'bb'","country":"'cn'"}
3 {"name":"'cc'","country":"'jp'"}
4 {"name":"'dd'","country":"'cn'"}
5 {"name":"'ee'","country":"'jp'"}
Time taken: 0.503 seconds, Fetched: 5 row(s)
# 指定查询 info:strcut 中的字段信息
hive (default)> select id,info.name,info.country from person_struct where info.name="'aa'";
OK
id name country
1 'aa' 'jp'
Time taken: 0.141 seconds, Fetched: 1 row(s)
类型转换现象指的是:在运算过程中,如果有不同的数据类型进行运算,则 Hive 会先将小的数据类型转换为大的数据类型后,再进行运算,转换的过程我们是看不见的,由编译器自动完成。
显示转换指的是:需要我们手动指定转换。
TINYINT
与 INT
- 所有的文本类型都可以隐式地转换成另一种文本类型。也可以被转换成 DOUBLE 或者 DECIMAL,转换失败时抛出异常。
- BOOLEAN 不能做任何的类型转换。
CAST
CAST('1' as INT)
如果转换失败,CAST 返回 NULL。
3.4 文件存储格式
Hive 会选择一个合适的底层数据存储文件格式,即使在不改变当前 Hive SQL 的情况下,性能也能得到数据级的提升。
Hive 数据存储常用的文件格式如下:
- 行式存储:
- 列式存储:
- 优化的行列式文件(ORCFILE)
RCFile
和ORCFile
我们先来看看这几种存储结构的优缺点:
- 水平的行存储结构:
select columnA from t_table_a
和select columnA,columnB,columnC,columnD from t_table_a
,这两个查询的开销差不多,都需要把所有的行读进来过一遍,筛选出需要的列。而且,在这种情况下,属于同一行的数据都在同一个 HDFS 块上,重建一行数据的成本比较低。
- 这样会大大降低查询执行的效率。
- 基于多个列做数据压缩时,由于不同的列数据类型和取值范围不同,压缩比不会太高
- 垂直的列存储结构:
每列单独存储或者将某几列作为列组存储在一起。
列存储在执行查询时可以避免读取不必要的列。而且一般同列的数据类型一致,取值范围相对多列混合更小,在这种情况下压缩数据能达到比较高的压缩比。
但这种结构在重建行时比较费劲,尤其当一行的多个列不在一个 HDFS 块上的时候。比如我们从第一个 DataNode 上拿到 columnA,从第二个 DataNode 上拿到了 columnB,有从第三个 DataNode 上拿到了 columnC,当把A,B,C拼成一行时,就需要把这三个列放到一起重建出行,需要比较大的网络开销和运算开销
行存储
和列存储
混合使用的一种结构,主要是传统数据库中提高 CPU 缓存利用率的一种方法,并不能直接用到 HDFS 中。但是RCFile
和ORCFile
这种结构是将
我们在创建表的时候可以指定以哪种文件格式进行存储,一旦创建完成后并不可修改。
SEQUENCEFILE、RCFILE、ORCFILE
格式的表不能直接从本地文件导入数据,数据要先导入到 TEXTFILE
格式的表中,然后再从表中使用 insert
导入 表中。
下面我们将详细了解这 5 种文件格式。
文本文件格式,是最简单的一种文件存储格式,也是 Hive 默认存储的格式。在导入数据时 Hive 会直接把数据文件复制到 HDFS
上但不进行处理。
TEXTFILE
的表:
create table if not exists t_textfile(
site string,
url string,
pv bigint,
label string
) row format delimited fields terminated by '\t' stored as textfile;
TEXTFILE
# 从 HDFS 上加载数据到表中
load data inpath '/data/weibo.txt' into table t_textfile;
TEXTFILE
直接使用 load 方式加载数据,其他存储格式则不能使用 load 直接导入数据文件。所以,TEXTFILE
的加载速度是最高的。
TEXTFILE
格式虽然可以使用 Gzip
压缩算法,但压缩后的文件不支持分割(split)。
在反序列化过程中,必须逐个字符判断是不是分隔符和行结束符,因此反序列化开销会比 SEQUENCEFILE
高几十倍。
3.4.2 SEQUENCEFILE
是 Hadoop API
提供的一种二进制文件支持,它具有使用方便、可分割、可压缩的特点。
SEQUENCEFILE
的内部合适取决于是否启用压缩,如果是压缩,则又可以分为记录压缩和块压缩。
SEQUENCEFILE
支持三种压缩选择:NONE、RECORD、BLOCK。
无压缩(NONE): 如果没有启动压缩(默认设置)那么每个记录就有它的记录长度(字节数)、键的长度,键和值组成。长度字段为 4 字节。
记录压缩(RECORD): 记录压缩格式与无压缩格式基本相同,不同的是:值字节是用定义在头部的编码器来压缩。注意:键是不压缩的。
块压缩(BLOCK): 块压缩一次压缩多个记录,因此它比记录压缩更紧凑,而且优先选择。当记录的字节达到最小大小,才会添加到块。该最小值由 io.seqfile.compress.blocksize
中的属性定义,默认值是 1000000 字节。格式为:记录数、键长度、键、值长度、值。Record 压缩率低,一般推荐使用 BLOCK 压缩。
我们来创建一个 SequenceFile
格式的 Hive 表:
create table if not exists t_sequencefile(
site string,
url string,
pv bigint,
label string
) row format delimited fields terminated by '\t' stored as sequencefile;
块压缩
hive (default)> set mapred.output.compression.type=BLOCK;
接着向 SequenceFile 表中加载数据:
insert overwrite table t_sequencefile select * from t_textfile;
SequenceFile 优点:
- 支持 split table,能够作为 MapReduce 的输入分片。
SequenceFile 缺点:
首先对表进行行划分,分成多个行组。一个行组主要包括:
- 区分一个 HDFS 块上的相邻行组;
- 元数据的头部信息主要包括该行组内的存储的行数、列的字段信息等等;
- 数据部分我们可以看出 RCFile 将每一行,存储为一列,将一列存储为一行
在一般的行存储中 select a from table
,虽然只是取出一个字段的值,但是还是会遍历整个表,所以效果和 select * from table
一样,在 RCFile 中,像前面说的情况,只会读取该行组的一行。
我们来创建一个 RCFile
create table if not exists t_rcfile(
site string,
url string,
pv bigint,
label string
) row format delimited fields terminated by '\t' stored as rcfile;
在存储空间上:
RCFile 是行划分,列存储,采用游程编码,相同的数据不会重复存储,很大程度上节约了存储空间,尤其是字段中包含大量重复数据的时候。
懒加载
数据存储到表中都是压缩的数据,Hive 读取数据的时候会对其进行解压缩,但是会针对特定的查询跳过不需要的列,这样也就省去了无用的列解压缩。
如:
select c from table where a > 1;
a > 1
的值,然后才去解压缩 c。若当前行组中不存在 a > 1
1、ORCFile 相比较 RCFile 的优点:
- 每个 task 只输出单个文件,这样可以减少 NameNode 的负载。
- 支持各种复杂的数据类型,比如:datetime,decimal,以及一些复杂类型(struct, list, map,等)。
2. ORCFile 的基本结构
ORCFile
在 RCFile 基础上引申出来 Stripe
和 Footer
等。
我们来看一下ORCFile 的文件结构示意图:
ORCFile 文件结构由三部分组成:
- :ORCFile 文件存储数据的地方。
- 文件脚注(file footer):包含了文件中 stripe 的列表,每个 stripe 的行数,以及每个列的数据类型。它还包含每个列的最小值、最大值、行计数、 求和等聚合信息。
- postscript
结构同样可以分为三部分:index data
、rows data
和 stripe footer
- :保存了所在条带的一些统计信息,以及数据在 stripe 中的位置索引信息。
- rows data:数据存储的地方,由多个行组构成,数据以流(stream)的形式进行存储。
- stripe footer
存储两部分的数据,即 metadata stream
和 data stream
- :用于描述每个行组的元数据信息。
- data stream
- :这一级的索引信息记录文件中所有 stripe 的位置信息,以及文件中所存储的每列数据的统计信息。
- 条带级别:该级别索引记录每个 stripe 所存储数据的统计信息。
1)程序可以借助 ORCFile 提供的索引加快数据查找和读取效率。
2)程序在查询 ORCFile 文件类型的表时,会先读取每一列的索引信息,将查找数据的条件和索引信息进行对比,找到满足查找条件的文件。
4)之后再根据 stripe 中每个行组的索引信息和查询条件比对的结果,找到满足要求的行组。
5)通过 ORCFile
这些索引,可以快速定位满足查询的数据块,规避大部分不满足查询条件的文件和数据块,相比于读取传统的数据文件,进行查找时需要遍历全部的数据,使用 ORCFile 可以避免磁盘和网络 I/O 的浪费,提升程序的查找效率,提升整个集群的工作负载。
3. ORCFile 的数据类型
Hive 在使用 ORCFile
文件进行存储数据时,描述这些数据的字段信息、字段类型信息及编码等相关信息都是和 ORCFile
中存储的数据放在一起的。
ORCFile
中每个块中的数据都是自描述的,不依赖外部的数据,也不存储在 Hive 的元数据库中。
ORCFile
- :包含 boolean(1bit)、tinyint(8bit)、smallint(16bit)、int(32bit)、bigint(64bit)。
- 浮点型:包含 float 和 double。
- 字符串类型:包含 string、char 和 varchar。
- 二进制类型:包含 binary。
- 日期和时间类型:包含 timestamp 和 date。
- 复杂类型
ORCFile
基本已经兼容了日常所能用到的绝大部分的字段类型。另外,ORCFile
4. ORCFile 相关的 Hive 配置
- :表示 ORCFile 文件的压缩类型,可选的类型有 NONE、ZLIB 和 SNAPPY,默认值是 ZLIB。
orc.compress.size
:表示压缩块(chunk)的大小,默认值是 262144(256KB)。orc.stripe.size
:写 stripe,可以使用的内存缓冲池大小,默认值是 67108864(64MB)。orc.row.index.stride
:行组级别索引的数据量大小,默认是 10000,必须要设置成大于等于 10000 的数。orc.create.index
:是否创建行组级别索引,默认是 true。orc.bloom.filter.columns
:需要创建布隆过滤的组。orc.bloom.filter.fpp
注:在 Hive 中使用布隆(bloom)过滤器,可以用较少的文件空间快速判定数据是否存在于表中,但是也存在将不属于这个表的数据判定为属于这个这表的情况,这个情况称之为假正概率,可以手动调整该概率,但概率越低,布隆过滤器所需要的空间越多。
是另外的一种高性能行列式的存储结构
1. Parquet 基本结构:
Parquet
Parquet
在存储数据时,也同 ORCFile
一样记录这些数据的元数据,这些元数据也同 Parquet
的文件结构一样,被分成多层文件级别的元数据、列块级别的元数据及页级别的元数据。
- 该文件的记录数;
- 该文件拥有的行组,以及每个行组的数据总量,记录数;
列块的元数据信息如下:
- 数据页的偏移量;
- 索引页的偏移量;
页头的元数据信息如下:
程序可以借助 Parquet
的这些元数据,在读取数据时过滤掉不需要读取的大部分文件数据,加快程序的运行速度。
同 ORCFile
的元数据一样,Parquet
的这些元数据信息能够帮助提升程序的运行速度,但是 ORCFile
在读取数据时又做了一定的优化,增强了数据的读取效率。在查询时所消耗的集群资源比 Parquet
类型少。
Parquet
ORCFile
多层级嵌套表达起来比较复杂,性能损失较大。
2. Parquet 的相关配置:
我们可以根据不同场景需求进行适当的参数调整,实现程序优化。
- :默认值为 134217728 byte,即 128MB,表示 RowGroup 在内存中的块大小。该值设置得大,可以提升 Parquet 文件的读取效率,但是相应在写的时候需要耗费更多的内存。
parquet.page.size
:默认值为 1048576 byte,即 1MB,表示每个页 (page)的大小。这个特指压缩后的页大小,在读取时会先将页的数据进行解压。页是 Parquet 操作数据的最小单位,每次读取时必须读完一整页的数据才能访问数据。这个值如果设置得过小,会导致压缩时出现性能问题。parquet.compression
:默认值为 UNCOMPRESSED(不压缩),表示页的压缩式。可以使用的压缩方式有 UNCOMPRESSED、SNAPPY、GZIP 和 LZO。parquet.enable.dictionary
:默认为 true,表示是否启用字典编码。parquet.dictionary.page.size
3.Parquet 和 ORC 压缩格式对比:
表类型 | 默认压缩 | 支持的压缩格式 | 描述 |
---|---|---|---|
ORCFile | Zlib | None、Zlib、Snappy | ORCFile 可以选择Zlib或Snappy压缩,Snappy需要额外安装 |
Parquet | Uncompressed | Uncompressed、Snappy、Gzip、Lzo | Parquet使用Gzip压缩率最高,使用 Lzo、Snappy效率高 |
ORCFile 表支持 None、Zlib、Snappy 压缩,默认为 ZLIB 压缩。但这 3 种压缩格式不支持切分,所以适合单个文件不是特别大的场景
Parquet 表支持 Uncompress、Snappy、Gzip、Lzo 压缩,默认不压缩(Uncompressed)。其中 Lzo 压缩是支持切分的,所以在表的单个文件较大的场景会选择 Lzo 格式。Gzip 方式压缩率高,效率低;而 Snappy、Lzo 效率高,压缩率低。