行式存储与列式存储
1)行存储
优点:数据被保存在一起了,insert和update更加容易,只有在查询满足条件的一整行数据时速度才会比较快
缺点:选择(selection)时即使只涉及某几列,所有数据也都会被读取,空间利用率不高。
例如:SELECT * FROM table;
2)列存储
优点:查询时只有涉及到的列会被读取;投影(projection)很高效;任何列都能作为索引。列式存储可以针对性的设计更好的设计压缩算法。
缺点:INSERT/UPDATE很麻烦或者不方便,不适合扫描小量的数据。
TEXTFILE和SEQUENCEFILE的数据格式是基于行存储的;
ORC和PARQUET的数据格式是基于列式存储的。
hive 表类型
Hive支持的表类型,或者称为存储格式有:TextFile、SequenceFile、RCFile、ORC、Parquet、AVRO。
TextFile
默认格式,数据不做压缩,磁盘开销大,数据解析开销大。
可结合Gzip、Bzip2使用(系统自动检查,执行查询时自动解压),但使用这种方式,hive不会对数据进行切分,从而无法对数据进行并行操作。在反序列化过程中,必须逐个字符判断是不是分隔符和行结束符,因此反序列化开销会比SequenceFile高几十倍。
create table if not exists textfile_table( site string, url string, pv bigint, label string) row format delimited fields terminated by '\t' stored as textfile; ##########################################插入数据######################################## set hive.exec.compress.output=true; --启用压缩格式 set mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec; --指定输出的压缩格式为Gzip set mapred.output.compress=true; set io.compression.codecs=org.apache.hadoop.io.compress.GzipCodec; insert overwrite table textfile_table select * from T_Name;
Sequence Files
Hadoop API提供的一种二进制文件,以key-value的形式序列化到文件中,存储方式为行式存储,sequencefile支持三种压缩选择:NONE,RECORD,BLOCK。Record压缩率低,一般建议使用BLOCK压缩,自身支持切片。
数据加载导入方式可以通过INSERT方式加载数据,现阶段基本不用
create table if not exists seqfile_table( site string, url string, pv bigint, label string) row format delimited fields terminated by '\t' stored as sequencefile; ##########################################插入数据######################################## set hive.exec.compress.output=true; --启用压缩格式 set mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec; --指定输出的压缩格式为Gzip set mapred.output.compression.type=BLOCK; --压缩选项设置为BLOCK set mapred.output.compress=true; set io.compression.codecs=org.apache.hadoop.io.compress.GzipCodec; insert overwrite table textfile_table select * from T_Name;
RCFile
存储方式:数据按行分块,每块按列存储。结合了行存储和列存储的优点:
- 首先,RCFile 保证同一行的数据位于同一节点,因此元组重构的开销很低
- 其次,像列存储一样,RCFile 能够利用列维度的数据压缩,并且能跳过不必要的列读取
- 数据追加:RCFile不支持任意方式的数据写操作,仅提供一种追加接口,这是因为底层的 HDFS当前仅仅支持数据追加写文件尾部。
- 行组大小:行组变大有助于提高数据压缩的效率,但是可能会损害数据的读取性能,因为这样增加了 Lazy 解压性能的消耗。而且行组变大会占用更多的内存,这会影响并发执行的其他MR作业。考虑到存储空间和查询效率两个方面,Facebook 选择 4MB 作为默认的行组大小,当然也允许用户自行选择参数进行配置。
列式存储表中,RCFile现在基本很少使用了,它是ORC表的前身,支持的功能和计算性能都低于ORC表。
${建表语句} stored as rcfile; -插入数据操作: set hive.exec.compress.output=true; set mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec; set mapred.output.compress=true; set io.compression.codecs=org.apache.hadoop.io.compress.GzipCodec; insert overwrite table rcfile_table select * from T_Name;
ORC
ORC (Optimized Row Columnar)文件格式为Hive数据提供了一种高效的存储方式。它的设计是为了克服其他Hive文件格式的限制。使用ORC文件可以提高Hive读写和处理数据时的性能。
1)Index Data:一个轻量级的index,默认是每隔1W行做一个索引。这里做的索引应该只是记录某行的各字段在 Row Data 中的 offset。
2)Row Data:存的是具体的数据,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个Stream来存储。
3)Stripe Footer:存的是各个 Stream 的类型,长度等信息。
每个文件有一个File Footer,这里面存的是每个 Stripe 的行数,每个 Column 的数据类型信息等;每个文件的尾部是一个 PostScript,这里面记录了整个文件的压缩类型以及 File Footer 的长度信息等。在读取文件时,会 seek 到文件尾部读 PostScript,从里面解析到 File Footer 长度,再读 File Footer ,从里面解析到各个 Stripe 信息,再读各个 Stripe ,即从后往前读
ORCFile格式的优点
- 每个task只输出单个文件,这样可以减少NameNode的负载;
- 支持各种复杂的数据类型,比如:datetime, decimal, 以及一些复杂类型(struct, list, map, and union);
- 在文件中存储了一些轻量级的索引数据;
- 基于数据类型的块模式压缩:a、integer类型的列用行程长度编码(run-length encoding);b、String类型的列用字典编码(dictionary encoding);
- 用多个互相独立的RecordReaders并行读相同的文件;
- 无需扫描markers就可以分割文件;
- 绑定读写所需要的内存;
- metadata的存储是用 Protocol Buffers的,所以它支持添加和删除一些列
Hive 中 ORC+Snappy 使用案例
-- SQL 建表语句 CREATE TABLE temperature_orc_snappy ( id string, year string, temperature int ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' STORED AS ORC TBLPROPERTIES ("orc.compress"="SNAPPY"); -- 加载数据 insert overwrite table temperature_orc_snappy select id, year, temperature from temperature; -- 查看结果 select * from temperature_orc_snappy;
Parquet
Parquet文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此Parquet格式文件是自解析的。
(1)行组(Row Group):每一个行组包含一定的行数,在一个HDFS文件中至少存储一个行组,类似于orc的stripe的概念。
(2)列块(Column Chunk):在一个行组中每一列保存在一个列块中,行组中的所有列连续的存储在这个行组文件中。一个列块中的值都是相同类型的,不同的列块可能使用不同的算法进行压缩。
(3)页(Page):每一个列块划分为多个页,一个页是最小的编码的单位,在同一个列块的不同页可能使用不同的编码方式。
通常情况下,在存储 Parquet 数据的时候会按照hdfs Block大小设置行组的大小,由于一般情况下每一个Mapper任务处理数据的最小单位是一个Block,这样可以把每一个行组由一个Mapper任务处理,增大任务执行并行度。Parquet文件的格式。
上图展示了一个 Parquet 文件的内容,一个文件中可以存储多个行组,文件的首位都是该文件的 Magic Code,用于校验它是否是一个 Parquet 文件,Footer length 记录了文件元数据的大小,通过该值和文件长度可以计算出元数据的偏移量,文件的元数据中包括每一个行组的元数据信息和该文件存储数据的 Schema 信息。
除了文件中每一个行组的元数据,每一页的开始都会存储该页的元数据,在Parquet中,有三种类型的页:数据页、字典页和索引页。数据页用于存储当前行组中该列的值,字典页存储该列值的编码字典,每一个列块中最多包含一个字典页,索引页用来存储当前行组下该列的索引,目前Parquet中还不支持索引页。
特点:
- 可以跳过不符合条件的数据, 只读取需要的数据, 降低IO数据量
- 压缩编码可以降低磁盘存储空间
- 只读取需要的列, 支持向量运算, 能够获取更好的扫描性能
- Parquet 格式是Spark SQL 的默认数据源, 可通过spark.sql.sources.default 配置
Hive 中 ORC+Snappy 使用案例
-- SQL 建表语句 CREATE TABLE temperature_parquet_snappy ( id string, year string, temperature int ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' STORED AS PARQUET tblproperties("parquet.compression"="SNAPPY"); -- 配置压缩的另外一种方式:命令行执行set parquet.compression=snappy; --- 加载数据 insert overwrite table temperature_parquet_snappy select id, year, temperature from temperature; -- 查看结果 SELECT * FROM temperature_parquet_snappy;
Avro Data Files
avro是一个数据序列化系统, 它提供:
- 丰富的数据结构
- 快速可压缩的二进制数据格式
- 存储持久数据的文件容器
- 远程过程调用RPC
- 简单的动态语言结合功能
- 实现模式:
- 二进制编码: Avro-specific 方式依赖代码(文件)生成特定类, 并内嵌JSON Schema
- JSON编码: Avro-generic 方式通过JSON文件动态加载Schema, 不需要编译加载直接就可以处理新的数据源
parquet vs orc ?
Parquet 与 ORC 的不同点总结以下:
嵌套结构支持:Parquet 能够很完美的支持嵌套式结构,而在这一点上 ORC 支持的并不好,表达起来复杂且性能和空间都损耗较大。
更新与 ACID 支持:ORC 格式支持 update 操作与 ACID,而 Parquet 并不支持。
压缩与查询性能:在压缩空间与查询性能方面,Parquet 与 ORC 总体上相差不大。可能ORC 要稍好于 Parquet。
查询引擎支持:这方面 Parquet 可能更有优势,支持 Hive、Impala、Presto 等各种查询引擎,而 ORC 与 Hive 接触的比较紧密,而与 Impala 适配的并不好。之前我们说 Impala 不支持 ORC,直到 CDH 6.1.x 版本也就是 Impala3.x 才开始以 experimental feature 支持 ORC 格式。
关于 Parquet 与 ORC,首先建议根据实际情况进行选择。另外,根据笔者的综合评估,如果不是一定要使用 ORC 的特性,还是建议选择 Parquet。
压缩怎么选呢?
1、ORC格式存储,Snappy压缩
2、Parquet格式存储,Lzo压缩
3、Parquet格式存储,Snappy压缩
所以在实际生产中,使用Parquet存储,lzo压缩的方式更为常见,这种情况下可以避免由于读取不可分割大文件引发的数据倾斜。但是,如果数据量并不大(预测不会有超大文件,若干G以上)的情况下,使用ORC存储,snappy压缩的效率还是非常高的
压缩
可使用以下三种标准对压缩方式进行评价
- 压缩比:压缩比越高,压缩后文件越小,所以压缩比越高越好
- 压缩时间:越快越好
- 已经压缩的格式文件是否可以再分割:可以分割的格式允许单一文件由多个 Mapper 程序处理,可以更好的并行化
Gzip压缩:
优点:压缩率比较高,压缩/解压速度也比较快,hadoop本身支持。
缺点:不支持分片。
应用场景:当每个文件压缩之后在1个block块大小内,可以考虑用gzip压缩格式,冷数据archive(归档)。
lzo压缩
优点:压缩/解压速度也比较快,合理的压缩率,支持分片,是Hadoop中最流行的压缩格式,支持Hadoop native库。
缺点:压缩率比gzip要低一些,Hadoop本身不支持,需要安装,如果支持分片需要建立索引,还需要指定inputformat改为lzo格式。
应用场景:一个很大的文本文件,压缩之后还大于200M以上的可以考虑,而且单个文件越大,lzo优点越明显。
snappy压缩
优点:支持Hadoop native库,高速压缩速度和合理的压缩率,hadoop-2.8.5自带;
缺点:不支持分片,压缩率比gzip要低;
应用场景:当MapReduce作业的map输出的数据比较大的时候,作为map到reduce的中间数据的压缩格式;Parquet文件的默认压缩方式。
bzip2压缩
优点:支持分片,具有很高的压缩率,比gzip压缩率都高,Hadoop本身支持,但不支持native。
缺点:压缩/解压速度慢,不支持Hadoop native库。
应用场景:适合对速度要求不高,但需要较高的压缩率的时候,可以作为mapreduce作业的输出格式,输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情况。
总结:
压缩比:bzip2 > gzip > lzo > snappy
压缩速度:snappy > lzo> gzip > bzip2
支持切片 : BZIp2 LZo