hive中的存储格式

Posted by JustDoDT on April 16, 2018

存储格式基本概念

Hive官网关于存储格式的介绍

官网介绍:

     Hive supports several file formats:
     * Text File
     * SequenceFile
     * RCFile
     * Avro Files
     * ORC Files
     * Parquet
     * Custom INPUTFORMAT and OUTPUTFORMAT

默认textfile的使用:

hive>create table t1(id int, name string) stored as textfile;
hive>create table t(id int, name string);

默认的存储格式是textfile,上面2条SQL语句是一样的效果。

hive>desc formatted t1;
    默认为textfile格式时候的格式:
	InputFormat:            org.apache.hadoop.mapred.TextInputFormat 
	OutputFormat:           org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
	Compressed:             No

hive>desc formatted t;
 	InputFormat:            org.apache.hadoop.mapred.TextInputFormat 
 	OutputFormat:           org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
 	Compressed:             No

产生的结果与textfile相同。 注意:inputformat与outputformat可以自己进行自定义

发展历程:

| TEXTFILE – (Default, depending on hive.default.fileformat configuration)
| RCFILE – (Note: Available in Hive 0.6.0 and later)
| ORC – (Note: Available in Hive 0.11.0 and later)
| PARQUET – (Note: Available in Hive 0.13.0 and later)
| AVRO – (Note: Available in Hive 0.14.0 and later)
| INPUTFORMAT input_format_classname OUTPUTFORMAT output_format_classname

建议:使用ORC和Parquet

行式存储 VS 列式存储

行式存储

hive行式存储

如上图所示,行式存储的结构: 假设文件中有ABCD 4列,5行数据

对应于HDFS里面就是Block

那么在Block里面是什么样的结构呢? 关注红框内的内容:

hive行式存储

行式存储带来的好处 对于每一行数据的每一列,必然是在同一个Block里面的。

行式存储带来的坏处 因为每一行记录有很多列,每一列的数据类型可能会是不一样的。

  • 如果每一列的数据类型都不一样,那么在压缩的时候,会带来麻烦, 每种类型的压缩比都不一样,无法达到每种类型数据压缩比达到最好的效果

  • select a,b from xxx; 对于行式存储,是需要将所有的列都给读取出来的 也就是说c和d我们虽然用不上,但是它也会去读,这样会造成IO的提升

列式存储

hive列式存储

如上图所示,列式存储的结构: 把每一行中的列给拆开了,存放在不同的Block里面了,列式存储不能保证同一行记录的所有列在同一个Block里面的,AB两列存在1个Block里,C列存在1个Block里,D列存在1个Block里。

  • 这样会带来一个很好的优势 既然是同一列的数据存储在同一个Block里面的,那么数据类型必然可以是一样的,就可以采用压缩比更高的方式进行压缩。

  • select c from xxx; 只需要拿走1个Block里面的数据就可以了,不用去碰其余两个Block里的数据 IO大幅度的减少

  • 缺点: 查询的东西一多,要读取的Block多

===>

如果我们使用select * from xxx; 行式和列式可能没有多大的区别,如果我们查的是某些字段,那么列式的优势就体现出来了。

在我们的大数据场景来说:可能我们有几百列,但是我们所查询的就只有几列,这种场景下,使用列式存储更好。

TextFile

默认的存储格式 普通文件/jsion/xml ====> 使用TextFile进行压缩

注意:使用TextFile进行压缩,很多都是当作字符串来进行处理的

SequenceFile

hive SequenceFile

如上图所示,Record是真正存放数据的,有3种级别:

  • No compression的Record: RecordLength KeyLength Key Value

  • Record compression的Record: RecordLength KeyLength Key CompressedValue

  • Block compression的Record: Number of records CompressedKeyLength CompressedKeys CompressedValueLengths CompressedValues

假设: record length = 10,key length = 4,那么value的length就是6,按照SequenceFile的存储形式,当我们读value的时候,拿到RecordLength和KeyLength,可以直接 跳过Key,直接去拿Value。 ===> 这样在查询的时候可能会好很多。

注意:Sequence用得很少

性能测试:

  hive>create table page_views_seq(
  track_time string,
  url string,
  session_id string,
  referer string,
  ip string,
  end_user_id string,
  city_id string
  )
  ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
  stored as sequencefile;

  hive>LOAD DATA LOCAL INPATH '/home/hadoop/data/data.log' OVERWRITE INTO TABLE page_views_seq;
  Loading data to table hive.page_views_seq
  Failed with exception Wrong file format. Please check the file's format.
  FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.MoveTask
  hive> 

会报错: Failed with exception Wrong file format. 原因: 对于SequenceFile来说,是不能直接load的 解决方法: 先在Hive表中创建一张textfile存储格式的表,然后再将这个表的数据给灌进来到SequenceFile为存储格式的表中来

hive>insert into table page_views_seq select * from page_views;

查看源文件大小

hive SequenceFile

使用SequenceFile后

[hadoop@hadoop001 ~]$ hdfs dfs -du -h /user/hive/warehouse/hive.db/page_views_seq
266.0 M  266.0 M  /user/hive/warehouse/hive.db/page_views_seq/000000_0
263.8 M  263.8 M  /user/hive/warehouse/hive.db/page_views_seq/000001_0
263.8 M  263.8 M  /user/hive/warehouse/hive.db/page_views_seq/000002_0
263.8 M  263.8 M  /user/hive/warehouse/hive.db/page_views_seq/000003_0
263.8 M  263.8 M  /user/hive/warehouse/hive.db/page_views_seq/000004_0
263.8 M  263.8 M  /user/hive/warehouse/hive.db/page_views_seq/000005_0
141.8 M  141.8 M  /user/hive/warehouse/hive.db/page_views_seq/000006_0

266 + 263.8 * 5 + 141.8 = 1726.8M > 1.63 *1024 = 1669.12M

为啥会比源文件大? 因为SequenceFile多了header等这些信息。SequenceFile是基于行式存储来做的现在生产环境上使用SequenceFile不多。并且是自动分片的,7个map。

RCFile(Record Columnar File)

hive SequenceFile

如上图所示,RCFile是Facebook开源的,FCFile是行列存储混合的(从上图也可以体现出来),一个Row Group 为4M。

性能测试

hive>create table page_views_rc(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
stored as rcfile;

hive>insert into table page_views_rc select * from page_views;

查看大小(使用RCFile)

[hadoop@hadoop001 ~]$ hdfs dfs -du -h /user/hive/warehouse/hive.db/page_views_rc/
229.5 M  229.5 M  /user/hive/warehouse/hive.db/page_views_rc/000000_0
227.6 M  227.6 M  /user/hive/warehouse/hive.db/page_views_rc/000001_0
227.6 M  227.6 M  /user/hive/warehouse/hive.db/page_views_rc/000002_0
227.6 M  227.6 M  /user/hive/warehouse/hive.db/page_views_rc/000003_0
227.6 M  227.6 M  /user/hive/warehouse/hive.db/page_views_rc/000004_0
227.6 M  227.6 M  /user/hive/warehouse/hive.db/page_views_rc/000005_0
122.4 M  122.4 M  /user/hive/warehouse/hive.db/page_views_rc/000006_0

229.5 + 227.6 * 5 + 122.4 = 1489.9M < 1.63 *1024 = 1669.12M

注意:默认也是支持分片的,也是7个map

ORC

其中,O指的是:Optimized(优化)。(Optimized Record Columnar File)

Hive官网关于ORC的介绍

ORC结构 hive SequenceFile

ORC存储结构如图所示,引入了以下几个概念: Stripe(一个Stripe为250M): Index Data + Row Data + Stripe Footer Index Data:轻量级的索引库,在一个Stripe里面,如果是数值类型的会存最大与最小值;如果是字符串会存前后缀; 这样存的好处是:

  • 当你的SQL语句后面有个where,你写了个id > 100,如果第一个Stripe里存的是0 ~ 99;第2个Stripe里存的是100~199; 第3个Stripe里存的是200~299;那么我们刚刚那个SQL语句,第1个Stripe就不会去读了。这样子,查询的性能肯定是会有提升的。

Stripe Footer:存的是类型; 这个不用太去关注。 查询数据的时候,先去查索引,如果不满足,那么这个Stripe就不会去读取了。这样就提升了性能

性能测试

采用压缩(默认使用的是Zlib压缩)

查看大小(使用ORC之后,并且采用压缩),也是支持分片的,7个mappers

  [hadoop@hadoop001 ~]$ hdfs dfs -du -h /user/hive/warehouse/hive.db/page_views_orc/
  68.9 M  68.9 M  /user/hive/warehouse/hive.db/page_views_orc/000000_0
  68.3 M  68.3 M  /user/hive/warehouse/hive.db/page_views_orc/000001_0
  68.3 M  68.3 M  /user/hive/warehouse/hive.db/page_views_orc/000002_0
  68.3 M  68.3 M  /user/hive/warehouse/hive.db/page_views_orc/000003_0
  68.3 M  68.3 M  /user/hive/warehouse/hive.db/page_views_orc/000004_0
  68.3 M  68.3 M  /user/hive/warehouse/hive.db/page_views_orc/000005_0
  36.7 M  36.7 M  /user/hive/warehouse/hive.db/page_views_orc/000006_0

68.9 + 68.3 * 5 + 36.7 = 477.2M < 1.63 *1024 = 1669.12M

不采用压缩

hive>create table page_views_orc_none 
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
stored as orc TBLPROPERTIES("orc.compress"="NONE");

报错: FAILED: SemanticException [Error 10043]: Either list of columns or a custom serializer should be specified

原因:没有指定数据源

hive>create table page_views_orc_none 
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
stored as orc TBLPROPERTIES("orc.compress"="NONE")
as select * from page_views;

查看大小(使用ORC之后,不采用压缩),也是支持分片的,7个map

[hadoop@hadoop001 ~]$ hdfs dfs -du -h /user/hive/warehouse/hive.db/page_views_orc_none/
182.0 M  182.0 M  /user/hive/warehouse/hive.db/page_views_orc_none/000000_0
180.5 M  180.5 M  /user/hive/warehouse/hive.db/page_views_orc_none/000001_0
180.5 M  180.5 M  /user/hive/warehouse/hive.db/page_views_orc_none/000002_0
180.5 M  180.5 M  /user/hive/warehouse/hive.db/page_views_orc_none/000003_0
180.5 M  180.5 M  /user/hive/warehouse/hive.db/page_views_orc_none/000004_0
180.5 M  180.5 M  /user/hive/warehouse/hive.db/page_views_orc_none/000005_0
97.0 M   97.0 M   /user/hive/warehouse/hive.db/page_views_orc_none/000006_0

182 + 180.5 * 5 + 97 = 1181.5 M < 1.63 *1024 = 1669.12M

Parquet

Hive官网关于parquet的介绍

性能测试

不采用压缩(默认是不采用压缩的)

hive>create table page_views_parquet(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
stored as parquet;

hive>insert into table page_views_parquet select * from page_views;

查看大小(使用Parquet后,不采用压缩),也是支持分片的,7个map

[hadoop@hadoop001 ~]$ hdfs dfs -du -h /user/hive/warehouse/hive.db/page_views_parquet/
192.8 M  192.8 M  /user/hive/warehouse/hive.db/page_views_parquet/000000_1
191.2 M  191.2 M  /user/hive/warehouse/hive.db/page_views_parquet/000001_1
191.2 M  191.2 M  /user/hive/warehouse/hive.db/page_views_parquet/000002_0
191.2 M  191.2 M  /user/hive/warehouse/hive.db/page_views_parquet/000003_0
191.2 M  191.2 M  /user/hive/warehouse/hive.db/page_views_parquet/000004_0
191.2 M  191.2 M  /user/hive/warehouse/hive.db/page_views_parquet/000005_0
101.8 M  101.8 M  /user/hive/warehouse/hive.db/page_views_parquet/000006_0

192.8 + 192.2 * 5 + 101.8 = 1255.6M < 1.63 * 1024 = 1669.12M

采用压缩(设置为Gzip压缩)

hive>set parquet.compression=GZIP;				// 设置压缩方式
hive>create table page_views_parquet_gzip(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
stored as parquet;

hive>insert into table page_views_parquet_gzip select * from page_views; 

查看大小(使用ORC之后,采用压缩),默认也是支持分片的,7个map

[hadoop@hadoop001 ~]$ hdfs dfs -du -h /user/hive/warehouse/hive.db/page_views_parquet_gzip/
64.5 M  64.5 M  /user/hive/warehouse/hive.db/page_views_parquet_gzip/000000_0
64.0 M  64.0 M  /user/hive/warehouse/hive.db/page_views_parquet_gzip/000001_0
64.0 M  64.0 M  /user/hive/warehouse/hive.db/page_views_parquet_gzip/000002_0
64.0 M  64.0 M  /user/hive/warehouse/hive.db/page_views_parquet_gzip/000003_0
64.0 M  64.0 M  /user/hive/warehouse/hive.db/page_views_parquet_gzip/000004_0
64.0 M  64.0 M  /user/hive/warehouse/hive.db/page_views_parquet_gzip/000005_0
34.5 M  34.5 M  /user/hive/warehouse/hive.db/page_views_parquet_gzip/000006_0

64.5 + 64.0 * 5 + 34.5 = 419M < 1.63 * 1024 = 1669.12M

各种存储格式的读性能测试

hive>select count(1) from page_views where session_id='xxxxxxx';
hive>select count(1) from page_views_seq where session_id='xxxxxxx';
hive>select count(1) from page_views_rc where session_id='xxxxxxx';
hive>select count(1) from page_views_orc where session_id='xxxxxxx';
hive>select count(1) from page_views_orc_none where session_id='xxxxxxx';
hive>select count(1) from page_views_parquet where session_id='xxxxxxx';
hive>select count(1) from page_views_parquet_gzip where session_id='xxxxxxx';

注: session_id以xxx代替,本文只是提供一种测试的思路

通过打印在控制台上的信息 去观察 HDFS Read,各种格式的对比如下:

存储格式 读的速度
textfile HDFS Read: 1755026906
seq HDFS Read: 1810686304
rc HDFS Read: 24384181
orc_zlib HDFS Read: 140918
orc_none HDFS Read: 1697274
parquet HDFS Read: 65041
parquet_gzip HDFS Read: 27936