第 3 章 Hive 数据类型和文件格式

本章我们将学习 Hive 数据类型和文件存储格式。Hive 中的数据类型主要分为:基本数据类型集合数据类型。我们先来看基本数据类型,这些数据类型和其他编程语言中的数据类型有什么区别吗?特别是 Java 语言。

3.1 基本数据类型

3.1.1 整数类型

Hive 主要有 4 种带符号的整数类型:TINYINTSMALINTINTBIGINT,它们分别对应 Java 中的 byteshortintlong。字节长度分别为1、2、4、8。如下表所示:

Hive 整数类型Java 整数类型字节长度后缀符例子
TINYINTbyte1 byte 有符号整数Y100Y
SMALINTshort2 byte 有符号整数S100S
INTint4 byte 有符号整数
BIGINTlong8 byte 有符号整数L100L

在使用整数字面量时,默认情况下为 INT,如果要声明为其他类型,通过后缀符来标识。

3.1.2 小数类型

Hive 中的小数类型主要有:FLOATDOUBLE 两种,分别对应 Java 中的 floatdouble,字节数分别为 32 和 64 位。如下表所示:

Hive 小数类型Java 小数类型字节长度后缀符例子
FLOATfloat4 byte 有符号整数F
DOUBLEdouble8 byte 有符号整数D
DECIMALBigDecimal表示任意精度的小数,通常在货币当中使用DECIMAL(5,2)

3.1.3 文本类型

Hive 中,主要有 3 种类型用于存储文本。

STRING 存储变长的文本,对长度没有限制。理论上将 STRING 可以存储的大小为 2GB,但是存储特别大的对象时效率可能受到影响,可以考虑使用 Sqoop 提供的大对象 支持。

VARCHAR 与 STRING 类似,但是长度上只允许在 1-65355 之间。

CHAR 则用固定长度来存储数据。

Hive 文本类型Java 文本类型描述例子
STRINGStringString 类型可以用单引号(’)或双引号(”)定义。'Hive 教程'
VARCHARvarchar 类型由长度定义,范围为 1-65355 ,如果存入的字符串长度超过了定义的长度,超出部分会被截断。尾部的空格也会作为字符串的一部分,影响字符串的比较。'Hive 教程'
CHARchar 是固定长度的,最大长度 255,而且尾部的空格不影响字符串的比较。'Hive 教程'

3.1.4 布尔类型

BOOLEAN:表示二元值的 truefalse

3.1.5 二进制数

BINARY:用于存储变长的二进制数据。

3.1.6 时间类型

TIMESTAMP 主要用来存储纳秒级别的时间戳,同时 Hive 提供了一些内置函数用于在 TIMESTAMP 与 Unix 时间戳(秒)和字符串之间做转换。比如:

cast(date as date)
cast(timestamp as date)
cast(string as date)
cast(date as string)

时间戳类型的数据不包含任务的时区信息,但是 to_utc_timestampfrom_utc_timestamp 函数可以用于时区转换。

DATE 类型则表示日期,对应年月日三个部分。

3.2 集合数据类型

有些时候,基本数据类型并不能满足我们的需求。在实际开发过程中,还可能会用到一些比较复杂的数据类型,同样,Hive 也给我们提供了 3 种集合数据类型:ARRAYMAPSTRUCT

3.2.1 ARRAY

ARRAY 是一组具有相同数据类型元素的集合,ARRAY 中的元素是有序的,每个元素都有一个编号,编号从开始,因此可以通过编号获取 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'

3.2.2 MAP

Map 是一种键值对形式的集合,通过 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;

3.2.3 STRUCT

STRUCT 类似于 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)

3.3 数据类型转换

类型转换现象指的是:在运算过程中,如果有不同的数据类型进行运算,则 Hive 会先将小的数据类型转换为大的数据类型后,再进行运算,转换的过程我们是看不见的,由编译器自动完成。

显示转换指的是:需要我们手动指定转换。

3.3.1 隐式转换

在 Hive 的类型层次中,可以根据需要进行隐式的类型转换,例如 TINYINTINT 相加,则会将 TINYINT 转化成 INT 然后 INT 做加法。隐式转换的规则大致可以归纳如下:

  • 任意数值类型都可以转换成更宽的数据类型(不会导致精度丢失)或者文本类型。
  • 所有的文本类型都可以隐式地转换成另一种文本类型。也可以被转换成 DOUBLE 或者 DECIMAL,转换失败时抛出异常。
  • BOOLEAN 不能做任何的类型转换。
  • 时间戳和日期可以隐式地转换成文本类型。

    3.3.2 显示转换

    显示转换主要使用 CAST 进行显式的类型转换,比如:

    CAST('1' as INT)

    如果转换失败,CAST 返回 NULL。

    3.4 文件存储格式

    Hive 会选择一个合适的底层数据存储文件格式,即使在不改变当前 Hive SQL 的情况下,性能也能得到数据级的提升。

    Hive 数据存储常用的文件格式如下:

    • 行式存储:
      • 文本格式(TEXTFILE)
      • 二进制序列化文件(SEQUENCEFILE)
    • 列式存储:
      • 行列式文件(RCFILE)
      • 优化的行列式文件(ORCFILE)
      • Apache Parquet

    注:RCFileORCFile 并不是纯粹的列式存储,它是基于行对数据表进行分组(行组),然后对行组进行列式存储。

    我们先来看看这几种存储结构的优缺点:

    • 水平的行存储结构:

      行存储模式就是把一整行存储在一起,包含所有的列,这是最常见的模式。这种结构能很好的适应动态的查询。比如:select columnA from t_table_aselect columnA,columnB,columnC,columnD from t_table_a ,这两个查询的开销差不多,都需要把所有的行读进来过一遍,筛选出需要的列。

      而且,在这种情况下,属于同一行的数据都在同一个 HDFS 块上,重建一行数据的成本比较低。

      这样做会带来两个问题:

      • 当一行中有很多列,而我们只需要其中很少的几个列时,我们也不得不把一行中所有的列读进来,然后从其中取出一些列。这样会大大降低查询执行的效率
      • 基于多个列做数据压缩时,由于不同的列数据类型和取值范围不同,压缩比不会太高
    • 垂直的列存储结构:

      列存储是将每列单独存储或者将某几列作为列组存储在一起

      列存储在执行查询时可以避免读取不必要的列。而且一般同列的数据类型一致,取值范围相对多列混合更小,在这种情况下压缩数据能达到比较高的压缩比。

      但这种结构在重建行时比较费劲,尤其当一行的多个列不在一个 HDFS 块上的时候。比如我们从第一个 DataNode 上拿到 columnA,从第二个 DataNode 上拿到了 columnB,有从第三个 DataNode 上拿到了 columnC,当把A,B,C拼成一行时,就需要把这三个列放到一起重建出行,需要比较大的网络开销和运算开销

    • 混合的 PAX 存储结构:这种结构是将行存储列存储混合使用的一种结构,主要是传统数据库中提高 CPU 缓存利用率的一种方法,并不能直接用到 HDFS 中。但是 RCFileORCFile 是继承自它的思想,先按行存再按列存。

    我们在创建表的时候可以指定以哪种文件格式进行存储,一旦创建完成后并不可修改。

    SEQUENCEFILE、RCFILE、ORCFILE 格式的表不能直接从本地文件导入数据,数据要先导入到 TEXTFILE 格式的表中,然后再从表中使用 insert 导入 SEQUENCEFILE、RCFILE、ORCFILE 表中。

    下面我们将详细了解这 5 种文件格式。

    3.4.1 TEXTFILE

    文本文件格式,是最简单的一种文件存储格式,也是 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

    SEQUENCEFILEHadoop 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 优点:

    • 支持基于记录(Record)或块(Block)的数据压缩。
    • 支持 split table,能够作为 MapReduce 的输入分片。
    • 修改简单:主要负责修改相应的业务逻辑,而不用考虑具体的存储格式。

    SequenceFile 缺点:

    • 需要一个合并文件的过程,且合并后的文件不方便查看。

    3.4.3 RCFILE

    RCFile 文件存储格式,首先将表分为几个行组,对每个行组内的数据进行按列存储,每一列的数据都是分开存储,正是先水平划分,再垂直划分的理念。

    RCFile

    首先对表进行行划分,分成多个行组。一个行组主要包括:

    • 16 字节的 HDFS 同步块信息,主要是为了区分一个 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 列进行解压缩,如果当前列中有 a > 1 的值,然后才去解压缩 c。若当前行组中不存在 a > 1 的列,那就不用解压缩 c,从而跳过整个行组。

    3.4.4 ORCFILE

    1、ORCFile 相比较 RCFile 的优点:

    1. ORCFile 扩展了 RCFile 的压缩,除了 Run-length(游程编码),引入了字典编码和 Bit 编码。
    2. 每个 task 只输出单个文件,这样可以减少 NameNode 的负载。
    3. 支持各种复杂的数据类型,比如:datetime,decimal,以及一些复杂类型(struct, list, map,等)。
    4. 文件是可切分(Split)的。在 Hive 中使用 ORCFile 作为表的文件存储格式,不仅节省 HDFS 存储资源,查询任务的输入数据量减少,使用的 MapTask 也就减少了。

    采用字典编码,最后存储的数据便是字典中的值,及每个字典值的长度以及字段在字典中的位置;

    采用 Bit 编码,对所有字段都可采用 Bit 编码来判断该列是否为 null, 如果为 null 则 Bit 值存为 0,否则存为 1,对于为 null 的字段在实际编码的时候不需要存储,也就是说字段若为 null,是不占用存储空间的。

    2. ORCFile 的基本结构

    ORCFile 在 RCFile 基础上引申出来 StripeFooter 等。

    每个 ORCFile 文件首先会被横向切分成多个 Stripe,而每个 Stripe 内部以列存储,所有的列存储在一个文件中,而且每个 stripe 默认的大小是 250MB,相对于 RCFile 默认的行组大小是 4MB,所以比 RCFile 更高效。

    我们来看一下ORCFile 的文件结构示意图:

    ORCFile

    ORCFile 文件结构由三部分组成:

    • 条带(stripe):ORCFile 文件存储数据的地方。
    • 文件脚注(file footer):包含了文件中 stripe 的列表,每个 stripe 的行数,以及每个列的数据类型。它还包含每个列的最小值、最大值、行计数、 求和等聚合信息。
    • postscript:含有压缩参数和压缩大小相关的信息。

    stripe 结构同样可以分为三部分:index datarows datastripe footer

    • index data:保存了所在条带的一些统计信息,以及数据在 stripe 中的位置索引信息。
    • rows data:数据存储的地方,由多个行组构成,数据以流(stream)的形式进行存储。
    • stripe footer:保存数据所在的文件目录。

    rows data 存储两部分的数据,即 metadata streamdata stream

    • metadata stream:用于描述每个行组的元数据信息。
    • data stream:存储数据的地方。

    ORCFile 在每个文件中提供了 3 个级别的索引:

    • 文件级:这一级的索引信息记录文件中所有 stripe 的位置信息,以及文件中所存储的每列数据的统计信息。
    • 条带级别:该级别索引记录每个 stripe 所存储数据的统计信息。
    • 在 stripe 中,每 10000 行构成一个行组,该级别的索引信息 就是记录这个行组中存储的数据的统计信息。

    1)程序可以借助 ORCFile 提供的索引加快数据查找和读取效率。

    2)程序在查询 ORCFile 文件类型的表时,会先读取每一列的索引信息,将查找数据的条件和索引信息进行对比,找到满足查找条件的文件。

    3)接着根据文件中的索引信息,找到存储对应的查询条件数据 stripe,再借助 stripe 的索引信息读文件中满足查询条件的所有 stripe 块。

    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。
    • 复杂类型:包含 struct、list、map 和 union 类型。

    目前 ORCFile 基本已经兼容了日常所能用到的绝大部分的字段类型。另外,ORCFile 中所有的类型都可以接受 NULL 值。

    4. ORCFile 相关的 Hive 配置

    表的属性配置项有如下几个:

    • orc.compress表示 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:使用布隆过滤器的假正(False Positive)概率,默认值是 0.05。

    注:在 Hive 中使用布隆(bloom)过滤器,可以用较少的文件空间快速判定数据是否存在于表中,但是也存在将不属于这个表的数据判定为属于这个这表的情况,这个情况称之为假正概率,可以手动调整该概率,但概率越低,布隆过滤器所需要的空间越多。

    3.4.5 Parquet

    Parquet 是另外的一种高性能行列式的存储结构,可以适用多种计算框架,被多种查询引擎所支持,包括 Hive、Impala、Drill 等。

    1. Parquet 基本结构:

    在一个 Parquet 类型的 Hive 表文件中,数据被分成多个行组,每个列块又被拆分成若干的页(Page),如下图所示:

    Parquet基本结构

    Parquet 在存储数据时,也同 ORCFile 一样记录这些数据的元数据,这些元数据也同 Parquet 的文件结构一样,被分成多层文件级别的元数据、列块级别的元数据及页级别的元数据。

    文件级别的元数据(fileMetadata)记录主要如下:

    • 表结构信息(Schema);
    • 该文件的记录数;
    • 该文件拥有的行组,以及每个行组的数据总量,记录数;
    • 每个行组下,列块的文件偏移量。

    列块的元数据信息如下:

    • 记录该列块的未压缩和压缩后的数据大小和压缩编码;
    • 数据页的偏移量;
    • 索引页的偏移量;
    • 列块的数据记录数。

    页头的元数据信息如下:

    • 该页的编码信息;
    • 该页的数据记录数。

    程序可以借助 Parquet 的这些元数据,在读取数据时过滤掉不需要读取的大部分文件数据,加快程序的运行速度。

    ORCFile 的元数据一样,Parquet 的这些元数据信息能够帮助提升程序的运行速度,但是 ORCFile 在读取数据时又做了一定的优化,增强了数据的读取效率。在查询时所消耗的集群资源比 Parquet 类型少。

    Parquet 在嵌套式结构支持比较完美,而 ORCFile 多层级嵌套表达起来比较复杂,性能损失较大。

    2. Parquet 的相关配置:

    我们可以根据不同场景需求进行适当的参数调整,实现程序优化。

    • parquet.block.size:默认值为 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:默认值为 1048576 byte,即 1MB。在使用字典编码时,会在 Parquet 的每行每列中创建一个字典页。使用字典编码,如果存储的数据页中重复的数据较多,能够起到一个很好的压缩效果,也能减少每个页在内存的占用。

    3.Parquet 和 ORC 压缩格式对比:

    表类型默认压缩支持的压缩格式描述
    ORCFileZlibNone、Zlib、SnappyORCFile 可以选择Zlib或Snappy压缩,Snappy需要额外安装
    ParquetUncompressedUncompressed、Snappy、Gzip、LzoParquet使用Gzip压缩率最高,使用 Lzo、Snappy效率高

    ORCFile 表支持 None、Zlib、Snappy 压缩,默认为 ZLIB 压缩。但这 3 种压缩格式不支持切分,所以适合单个文件不是特别大的场景。使用 Zlib 压缩率高,但效率差一些;使用 Snappy 效率高,但压缩率低。

    Parquet 表支持 Uncompress、Snappy、Gzip、Lzo 压缩,默认不压缩(Uncompressed)。其中 Lzo 压缩是支持切分的,所以在表的单个文件较大的场景会选择 Lzo 格式。Gzip 方式压缩率高,效率低;而 Snappy、Lzo 效率高,压缩率低。