hive详解之hive数据存储

hive数据存储格式

HIve的文件存储格式有四种:TEXTFILE 、SEQUENCEFILE、ORC、PARQUET,前面两种是行式存储,后面两种是列式存储;所谓的存储格式就是在Hive建表的时候指定的将表中的数据按照什么样子的存储方式,如果指定了A方式,那么在向表中插入数据的时候,将会使用该方式向HDFS中添加相应的数据类型。

如果为textfile的文件格式,直接load就OK,不需要走MapReduce;如果是其他的类型就需要走MapReduce了,因为其他的类型都涉及到了文件的压缩,这需要借助MapReduce的压缩方式来实现。

只需要在创建表的时候告诉 Hive 数据中的列分隔符和行分隔符,Hive 就可以解析数据。

比对三种主流的文件存储格式TEXTFILE 、ORC、PARQUET

压缩比:ORC >  Parquet >  textFile(textfile没有进行压缩)

压缩比案例

1,textfile,创建表,存储数据格式为TEXTFILE
create table log_text (
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 textfile ;
 
向表中加载数据
 
load data local inpath '/opt/module/datas/log.data' into table log_text ;
查看表中数据大小
 
这个过程不会走MapReduce,而是直接将文件上传到了HDFS,在HDFS上文件的名字还叫log.data
dfs -du -h /user/hive/warehouse/db_hive.db/log_text;
18.1 M  /user/hive/warehouse/db_hive.db/log_text/log.data

2,ORC,创建表,存储数据格式为ORC
create table log_orc(
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 orc ;
 
向表中加载数据
insert into table log_orc select * from log_text ;
 
查看表中数据大小
这个过程要走MapReduce,而且文件是按照列式存储的,还会对文件进行压缩,Orc默认使用的压缩方式是
zlib因此会更加节省空间,hadfs上是新的文件名,
 
hive (db_hive)> dfs -du -h /user/hive/warehouse/db_hive.db/log_orc;
2.8 M  /user/hive/warehouse/db_hive.db/log_orc/000000_0
 
 
3,Parquet,创建表,存储数据格式为parquet
create table log_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 ; 
 
向表中加载数据
insert into table log_parquet select * from log_text ;
 
查看表中数据大小这个过程要走MapReduce,而且文件是按照列式存储的,因此会更加节省空间,
hadfs上是新的文件名,
hive (db_hive)> dfs -du -h /user/hive/warehouse/db_hive.db/log_parquet;
13.1 M  /user/hive/warehouse/db_hive.db/log_parquet/000000_0
 
存储文件的压缩比总结:
ORC >  Parquet >  textFile
 
 
select count(*) from log_text;
 
select count(*) from log_orc;
 
select count(*) from log_parquet;
 
由于本身数据有点少,存储文件的查询速度总结:查询速度相近。

解释:hdfs dfs -du -h 输出三列数据的含义

命令 hdfs dfs -du -h /data/

结果 102.3 M 307.0 M /data/

第一列标示该目录下总文件大小

第二列标示该目录下所有文件在集群上的总存储大小和你的副本数相关,我的副本数是3 ,所以

第二列的是第一列的三倍 (第二列内容=文件大小*副本数)

第三列标示你查询的目录

自定义压缩格式

在建表的时候,如果我们指定了列式存储的方式,他会默认使用对于的压缩方式将我们的数据进行压缩,与此同时我们能够自己定制在文件存储的时候使用什么样子的压缩方式,例子如下:

1.创建一个非压缩的的ORC存储方式
create table log_orc_none(
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 orc tblproperties ("orc.compress"="NONE");
插入数据
hive (default)> insert into table log_orc_none select * from log_text ;
 
2.创建一个SNAPPY压缩的ORC存储方式
create table log_orc_snappy(
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 orc tblproperties ("orc.compress"="SNAPPY");
插入数据
hive (default)> insert into table log_orc_snappy select * from log_text ;
 
3.创建一个默认压缩的ORC存储方式
create table log_orc(
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 orc ;
 
向表中加载数据
insert into table log_orc select * from log_text ;
 
对比三者的压缩比:
 
hive (db_hive)> dfs -du -h /user/hive/warehouse/db_hive.db/log_orc_none;
18.1 M  /user/hive/warehouse/db_hive.db/log_orc_none/log.data
 
hive (db_hive)> dfs -du -h /user/hive/warehouse/db_hive.db/log_orc_snappy;
3.8 M  /user/hive/warehouse/db_hive.db/log_orc_snappy/000000_0
 
hive (db_hive)> dfs -du -h /user/hive/warehouse/db_hive.db/log_orc;
2.8 M  /user/hive/warehouse/db_hive.db/log_orc/000000_0
 
总结:
没有压缩的orc格式相当于textfile,默认的压缩格式压缩比最大,snappy对数据进行了压缩
orc存储文件默认采用ZLIB压缩,ZLIB采用的是deflate压缩算法。因此比snappy压缩的小。
文件没有压缩的话,HDFS上显示的是原来的文件名,如果压缩的话,使用类似于000000_0的文件名

解释:TBLPROPERTIES

实际上就是table properties,TBLPROPERTIES允许开发者定义一些自己的键值对信息。可以对TBLPROPERTIES进行查看和修改(部分可修改)。在TBLPROPERTIES中有一些预定义信息,比如last_modified_user和last_modified_time,其他的一些预定义信息包括:

TBLPROPERTIES ("comment"="table_comment")
TBLPROPERTIES ("hbase.table.name"="table_name")
TBLPROPERTIES ("immutable"="true") or ("immutable"="false") 
TBLPROPERTIES ("orc.compress"="ZLIB") or ("orc.compress"="SNAPPY") or ("orc.compress"="NONE")
TBLPROPERTIES ("transactional"="true") or ("transactional"="false")
TBLPROPERTIES ("NO_AUTO_COMPACTION"="true") or ("NO_AUTO_COMPACTION"="false"), the default is "false" 
TBLPROPERTIES ("compactor.mapreduce.map.memory.mb"="mapper_memory") 
TBLPROPERTIES ("compactorthreshold.hive.compactor.delta.num.threshold"="threshold_num") 
TBLPROPERTIES ("compactorthreshold.hive.compactor.delta.pct.threshold"="threshold_pct") 
TBLPROPERTIES ("auto.purge"="true") or ("auto.purge"="false") 
TBLPROPERTIES ("EXTERNAL"="TRUE")

(1)comment:可以用来定义表的描述信息

(2)hbase.table.name:hive通过 storage handler(暂放)将hive与各种工具联系起来,这是是使用hive接入hbase时,设置的属性(暂放)

(3)immutable:顾名思义‘不可变的’,当表的这个属性为true时,若表中无数据时可以insert数据,但是当表已经有数据时,insert操作会失败。不可变表用来防止意外更新,避免因脚本错误导致的多次更新,而没有报错。本人实际中还没用到这个属性。

(4)orc.compress:这是orc存储格式表的一个属性,用来指定orc存储的压缩方式(暂放)。

(5) transactional,NO_AUTO_COMPACTION,compactor.mapreduce.map.memory.mb, compactorthreshold.hive.compactor.delta.num.threshold, compactorthreshold.hive.compactor.delta.pct.threshold:这5个属性与hive的事务支持有关,先不做了解。

(6)auto.purge:当设置为ture时,删除或者覆盖的数据会不经过回收站,直接被删除。配置了此属性会影响到这些操作: Drop Table, Drop Partitions, Truncate Table,Insert Overwrite.

(7)EXTERNAL:通过修改此属性可以实现内部表和外部表的转化。

修改tblproperties

ALTER TABLE table_name SET TBLPROPERTIES (property_name = property_value, property_name = property_value, ... );

hive数据模型

Hive 中所有的数据都存储在 HDFS 中,Hive 中包含以下数据模型:Table,External Table,Partition,Bucket即内部表、外部表、分区表和桶表。

内部表

内部表也称为管理表。因为这种表,Hive会或多或少地控制数据的生命周期。Hive默认情况下回将这些表的数据存储在由配置项hive.metastore.warehouse.dir所定义的目录(比如/user/hive/warehouse)的子目录下。

如果我有一个表test,那么在HDFS中会创建/user/hive/warehouse/test目录(这里假定hive.metastore.warehouse.dir配置为/user/hive/warehouse);test表所对应的所有数据都存放在这个目录中。

如果删除这张表,则表在关系数据中存储的元数据以及在warehouse目录下的数据也会被清除掉。

同时管理表不方便与其他工作共享数据。例如我们有一份由Pig或者其他工具创建并且主要由这一工具使用的数据,同时我们还想使用Hive在这份数据上执行一些查询,可是并没有给予Hive对数据的所有权,我们可以创建一个外部表指向这份数据,而并不需要对其具有所有权。

外部表

为了避免潜在产生混淆的可能性,如果用户不想使用默认的表路径,那么最好是使用外部表。

外部表可以读取指定目录下的以逗号为分隔的数据:

CREATE EXTERNAL TABLE IF NOT EXISTS stocks(
 exchange   STRING,
 symbol     STRING,
 ymd        STRING,
 price_open FLOAT,
 price_high FLOAT,
 volume     INT,
 price_adj_close  FLOAT)
ROW FORMAT DELIMITED FIFLDS TERMINATED BY ',' --逗号分隔文件
LOCATION '/data/stocks'  --指定Hive数据的路径

因为表是外部的,所以Hive并非认为完全拥有这份数据,从而删除该表的时候不会删除这份数据。不过描述表的元数据信息会被删除掉。

内部表和外部表的区别

1.未被external修饰的是内部表(managed table),被external修饰的为外部表(external table)

2.Hive 创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。

3.在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。这样外部表相对来说更加安全些,数据组织也更加灵活,方便共享源数据。

4.内部表数据由Hive自身管理,外部表数据由HDFS管理;内部表数据存储的位置是hive.metastore.warehouse.dir(默认:/user/hive/warehouse),外部表数据的存储位置由自己制定(如果没有LOCATION,Hive将在HDFS上的/user/hive/warehouse文件夹下以外部表的表名创建一个文件夹,并将属于这个表的数据存放在这里);

5.对内部表的修改会将修改直接同步给元数据,而对外部表的表结构和分区进行修改,则需要修复(MSCK REPAIR TABLE table_name;)

对于什么时候使用外部表,什么时候内部表,并没有一个固定的规范,通过最近一段时间的观察,大概总结了一下几点:

1.做etl处理时,通常会选择内部表做中间表,因为清理时,会将HDFS上的文件同时删除

2.如果怕误删数据,可以选择外部表,因为不会删除文件,方便恢复数据

3.如果对数据的处理都是通过hql语句完成,选择内部表,如果有其他工具一同处理,选择外部表

借鉴学习:

分区表

分区表用于水平分散压力,将数据从物理上转移到和使用最频繁的用户更近的地方。分区是以字段的形式在表结构中存在,通过describe table命令可以查看到字段存在, 但是该字段不存放实际的数据内容,仅仅是分区的表示(伪列)。分区分为静态分区和动态分区

(1)静态分区

静态分区表:所谓的静态分区表指的就是,我们在创建表的时候,就已经给该表中的数据定义好了数据类型,在进行加载数据的时候,我们已经知道该数据属于什么类型,并且直接加载到该分区内就可以了。来看一个简单的分区表的创建语句(这里创建的是一张内部表):

create table if not exists sopdm.wyp2(id int,name string,tel string)

partitioned by(age int)

row format delimited

fields terminated by ','

stored as textfile;

–overwrite是覆盖,into是追加

insert into table sopdm.wyp2

partition(age='25')

select id,name,tel from sopdm.wyp;

(2)动态分区
动态分区表:所谓的动态分区表,其实建表方式跟静态分区表没有区别,最主要的区别是在载入数据的时候,静态分区表我们载入数据之前必须保证该分区存在,并且我么已经明确知道载入的数据的类型,知道要将数据加载到那个分区当中去,而动态分区表,在载入的时候,我们事先并不知道该条数据属于哪一类,而是需要hive自己去判断该数据属于哪一类,并将该条数据加载到对应的目录中去。建表语句跟静态分区表的建表语句相同,这里不再赘述,主要来看看数据的加载:

–设置为true表示开启动态分区功能(默认为false)
set hive.exec.dynamic.partition=true;
–设置为nonstrict,表示允许所有分区都是动态的(默认为strict)
set hive.exec.dynamic.partition.mode=nonstrict;
–insert overwrite是覆盖,insert into是追加
set hive.exec.dynamic.partition.mode=nonstrict;
insert overwrite table sopdm.wyp2

partition(age)

select id,name,tel,age from sopdm.wyp;
insert overwrite table sopdm.wyp2

partition(age)

select id,name,tel,age from sopdm.wyp;

分桶表

分桶之前要执行命令set hive.enforce.bucketiong=true;

CREATE TABLE bucketed_user (id INT) name STRING)

CLUSTERED BY (id) INTO 4 BUCKETS;

对于每一个表(table)或者分区, Hive可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分。Hive也是 针对某一列进行桶的组织。Hive采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中。

把表(或者分区)组织成桶(Bucket)有两个理由:

(1)获得更高的查询处理效率。桶为表加上了额外的结构,Hive 在处理有些查询时能利用这个结构。具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可以使用 Map 端连接 (Map-side join)高效的实现。比如JOIN操作。对于JOIN操作两个表有一个相同的列,如果对这两个表都进行了桶操作。那么将保存相同列值的桶进行JOIN操作就可以,可以大大较少JOIN的数据量。

(2)使取样(sampling)更高效。在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。

分区又分桶

可以对数据表分区之后继续分桶。

hive存储路径 hive存储格式_数据

但是分区之后继续分桶,我们在hdfs文件系统上看不出分桶的多个数据表文件,只能看见一个文件,但是能从文件路径上看出分区的信息。

hive存储路径 hive存储格式_hive_02


看看分区又分桶的查询结果:

hive存储路径 hive存储格式_hive存储路径_03