- 1、分区表
- 1.1 分区表基本语法
- (1)创建分区表
- (2)分区表读写数据
- (3)分区表基本操作
- 1.2 二级分区
- 1.3 动态分区
- 2、分桶表
- 2.1 分桶表的基本语法
- 2.2 分桶排序表
- 3、文件格式与压缩
- 3.1 Hadoop压缩概述
- 3.2 Hive文件格式
- (1)Text File
- (2)ORC
- (3)Parquet
- 3.3 压缩
- (1)Hive表数据进行压缩
- (2)计算过程中使用压缩
1、分区表
Hive中的分区就是把一张大表的数据按照业务需要分散的存储到多个目录,每个目录就称为该表的一个分区。在查询时通过where子句中的表达式选择查询所需要的分区,这样的查询效率会提高很多。
1.1 分区表基本语法
(1)创建分区表
create table dept_partition
(deptno int, --部门编号dname string, --部门名称loc string --部门位置
)partitioned by (day string)row format delimited fields terminated by '\t';
(2)分区表读写数据
1)写数据
(1)load
- 数据准备
在/opt/module/hive/datas/路径上创建文件dept_20220401.log,并输入如下内容。
[root@hadoop102 datas]$ vim dept_20220401.log
10 行政部 1700
20 财务部 1800
无论是哪种方式,只要往分区表里面写入数据,都必须声明是哪个分区。
- 装载语句
hive (default)>
load data local inpath '/opt/module/hive/datas/dept_20220401.log'
into table dept_partition
partition(day='20220401');
(2)insert
将day='20220401’分区的数据插入到day='20220402’分区,可执行如下装载语句
hive (default)>
insert overwrite table dept_partition partition (day = '20220402')
select deptno, dname, loc
from dept_partition
where day = '2020-04-01';
查询出来之后插入
2)读数据
查询分区表数据时,可以将分区字段看作表的伪列,可像使用其他字段一样使用分区字段。
select deptno, dname, loc ,day
from dept_partition
where day = '2020-04-01';
可以看到存储的时候只有三个字段,但是查询的时候可以查询到四个字段,原因是因为,第四个分区字段取自于路径的信息。
(3)分区表基本操作
1)查看所有分区信息
hive> show partitions dept_partition;
2)增加分区
(1)创建单个分区
hive (default)>
alter table dept_partition
add partition(day='20220403');
- 新增分区,但此时新的分区里面是没有数据的。
那么新建一个分区会做哪些事情呢?
1、首先在HDFS上创建一个路径。
2、会在hive的元数据里面增加一条分区信息。【分区信息也属于hive元数据的一部分】
(2)同时创建多个分区(分区之间不能有逗号)
hive (default)>
alter table dept_partition
add partition(day='20220404') partition(day='20220405');
3)删除分区
(1)删除单个分区
hive (default)>
alter table dept_partition
drop partition (day='20220403');
(2)同时删除多个分区(分区之间必须有逗号)
hive (default)>
alter table dept_partition
drop partition (day='20220404'), partition(day='20220405');
- 之前有提及内部表和外部表:
两者的区别是在删除表的时候,对HDFS的处理上面。如果是外部表,则在删除的时候,不会删除HDFS上对应的路径,只会删除元数据;如果是内部表,则在删除的时候,会删除HDFS上对应的路径以及元数据。 - 这个内部表与外部表对分区同样是适用的。
如果建的是外部表,则删除分区的时候,也不会删除HDFS上的路径。
4)修复分区
Hive将分区表的所有分区信息都保存在了元数据中,只有元数据与HDFS上的分区路径一致时,分区表才能正常读写数据。若用户手动创建/删除分区路径,Hive都是感知不到的,这样就会导致Hive的元数据和HDFS的分区路径不一致。再比如,若分区表为外部表,用户执行drop partition命令后,分区元数据会被删除,而HDFS的分区路径不会被删除,同样会导致Hive的元数据和HDFS的分区路径不一致。
若出现元数据和HDFS路径不一致的情况,可通过如下几种手段进行修复。
(1)add partition
若手动创建HDFS的分区路径,Hive无法识别,可通过add partition命令增加分区元数据信息,从而使元数据和分区路径保持一致。
(2)drop partition
若手动删除HDFS的分区路径,Hive无法识别,可通过drop partition命令删除分区元数据信息,从而使元数据和分区路径保持一致。
(3)msck
若分区元数据和HDFS的分区路径不一致,还可使用msck命令进行修复,以下是该命令的用法说明。
hive (default)>
msck repair table table_name [add/drop/sync partitions];
说明:
- msck repair table table_name add partitions:该命令会增加HDFS路径存在但元数据缺失的分区信息。
- msck repair table table_name drop partitions:该命令会删除HDFS路径已经删除但元数据仍然存在的分区信息。
- msck repair table table_name sync partitions:该命令会同步HDFS路径和元数据分区信息,相当于同时执行上述的两个命令。
- msck repair table table_name:等价于msck repair table table_name add partitions命令。【默认值】
上面这些命令修改的都是元数据,而不会去改变HDFS的路径。
我们在执行分区sql语句的时候,实际上是以mysql里面的元数据为主的。只有元数据里面有的才能查出来。
1.2 二级分区
- 首先,一级分区指的是将一张表的数据分散的存储到多个路径下面。
- 二级分区指的是将分区里面的数据再进行进一步的分散存储。甚至还能有三、四级分区,可以一层一层往下分。
在什么情况下会需要使用二级分区?
- 将来在hive做项目的时候,大多数使用的都是分区表。对于分区表而言,很重要的一点就是按照什么分区。其实绝大多数的分区表都是按照日期分区的。hive做的是离线批处理,攒一批进行处理。
- 如果觉得一天进行一次处理太慢了,想一个小时处理一次,这个时候就可以进行二级分区。
1)二级分区表建表语句
hive (default)>
create table dept_partition2(deptno int, -- 部门编号dname string, -- 部门名称loc string -- 部门位置
)
partitioned by (day string, hour string)
row format delimited fields terminated by '\t';
- 可以看到二级分区相比于一级分区就是在分区的时候多加了一个字段,但是要注意顺序不要颠倒,day是一级分区的路径,hour是二级分区。
2)数据装载语句
hive (default)>
load data local inpath '/opt/module/hive/datas/dept_20220401.log'
into table dept_partition2
partition(day='20220401', hour='12');
- 装载数据没有很大的差别。
3)查询分区数据
hive (default)>
select *
from dept_partition2
where day='20220401' and hour='12';
1.3 动态分区
动态分区是指向分区表insert数据时,被写往的分区不由用户指定,而是由每行数据的最后一个字段的值来动态的决定。使用动态分区,可只用一个insert语句将数据写入多个分区。
1)动态分区相关参数
(1)动态分区功能总开关(默认true,开启)
set hive.exec.dynamic.partition=true
(2)严格模式和非严格模式
动态分区的模式,默认strict(严格模式),要求必须指定至少一个分区为静态分区==【也就是必须指定一个分区字段】==,nonstrict(非严格模式)允许所有的分区字段都使用动态分区。
set hive.exec.dynamic.partition.mode=nonstrict
(3)一条insert语句可同时创建的最大的分区个数,默认为1000。
set hive.exec.max.dynamic.partitions=1000
- 实际上mysql里面不易设置太多的分区字段,因为设置一个分区,就会在元数据里面插入一个数据,如果分区太多,则mysql的分区表需要保存非常多的数据。
(4)单个Mapper或者Reducer可同时创建的最大的分区个数,默认为100。
set hive.exec.max.dynamic.partitions.pernode=100
- 一个insert语句将来一定会翻译成mapreduce语句去执行的,而有些简单的sql语句可能是只有map而没有reduce
(5)一条insert语句可以创建的最大的文件个数,默认100000。
hive.exec.max.created.files=100000
(6)当查询结果为空时且进行动态分区时,是否抛出异常,默认false。
hive.error.on.empty.partition=false
- 上面这些都是和动态分区相关的参数,都可能导致分区报错。
2)案例实操
需求:将dept表中的数据按照地区(loc字段),插入到目标表dept_partition_dynamic的相应分区中。
(1)创建目标分区表
hive (default)>
create table dept_partition_dynamic(id int, name string
)
partitioned by (loc int)
row format delimited fields terminated by '\t';
(2)设置动态分区
set hive.exec.dynamic.partition.mode = nonstrict;
hive (default)>
insert into table dept_partition_dynamic
partition(loc)
select deptno, dname, loc
from dept;
(3)查看目标分区表的分区情况
hive (default)> show partitions dept_partition_dynamic;
2、分桶表
分区提供一个隔离数据和优化查询的便利方式。不过,并非所有的数据集都可形成合理的分区。对于一张表或者分区,Hive 可以进一步组织成桶,也就是更为细粒度的数据范围划分,分区针对的是数据的存储路径,分桶针对的是数据文件。
分桶表的基本原理是,首先为每行数据计算一个指定字段的数据的hash值,然后模以一个指定的分桶数,最后将取模运算结果相同的行,写入同一个文件中,这个文件就称为一个分桶(bucket)。需要对表的数据进行hash分区
- 分区和分桶没有必然的联系,是两个不同的概念。分区表是将数据存储到不同的分区里面,分桶表是将数据存储到不同的文件里面。
- 一张表可以既是分区表也是分桶表。对于分区分桶表,会对每一个分区里面的数据进行分桶。
- 不能对同一个字段既分区又分桶。因为如果一个字段分区了,那么这个字段在这个分区里面都是相同的,再分桶也是分到同一个桶里面,是没有意义的。而且分区字段还是一个虚拟字段。
2.1 分桶表的基本语法
1)建表语句
hive (default)>
create table stu_buckt(id int, name string
)
clustered by(id)
into 4 buckets
row format delimited fields terminated by '\t';
2.2 分桶排序表
- 分桶字段和排序字段不要求完全一致,并且分桶字段和排序字段都可以有多个。
3、文件格式与压缩
- hadoop支持哪些压缩算法,hive就支持哪些压缩算法,两者保持一致。
3.1 Hadoop压缩概述
压缩格式 | 算法 | 文件扩展名 | 是否可切分 |
---|---|---|---|
DEFLATE | DEFLATE | .deflate | 否 |
Gzip | DEFLATE | .gz | 否 |
bzip2 | bzip2 | .bz2 | 是 |
LZO | LZO | .lzo | 是 |
Snappy | Snappy | .snappy | 否 |
3.2 Hive文件格式
为Hive表中的数据选择一个合适的文件格式,对提高查询性能的提高是十分有益的。Hive表数据的存储格式,可以选择text file、orc、parquet、sequence file
等。
(1)Text File
文本文件是Hive默认使用的文件格式,文本文件中的一行内容,就对应Hive表中的一行记录。
可通过以下建表语句指定文件格式为文本文件:
create table textfile_table
(column_specs)
stored as textfile;
- 当不声明文件格式的时候,默认使用的是文本文件。
(2)ORC
1)文件格式
ORC(Optimized Row Columnar)file format是Hive 0.11版里引入的一种列式存储的文件格式。ORC文件能够提高Hive读写数据和处理数据的性能。
与列式存储相对的是行式存储,下图是两者的对比:
如图所示左边为逻辑表,右边第一个为行式存储,第二个为列式存储。
(1)行存储的特点
查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。【这种情况下使用行式存储的格式较好。】
(2)列存储的特点
因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。
前文提到的text file和sequence file都是基于行存储的,orc和parquet是基于列式存储的。
- 我们平常的查询大多数是以按列查询为主,过滤等按行查询的操作其实不多。
- 文本文件、sequence file是行式存储的。
- orc、parquet是列式存储的。
orc文件的具体结构如下图所示:
行的位置是稀疏存储的,每一万行记录一个索引。
- 尾部会对数据进行编码,这样能减少对存储空间的占用率。
- 每个Orc文件由Header、Body和Tail三部分组成。
- 其中Header内容为ORC,用于表示文件类型。
- Body由1个或多个stripe组成,每个stripe一般为HDFS的块大小,每一个stripe包含多条记录,这些记录按照列进行独立存储,每个stripe里有三部分组成,分别是Index Data,Row Data,Stripe Footer。
- Index Data:一个轻量级的index,默认是为各列每隔1W行做一个索引。每个索引会记录第n万行的位置,和最近一万行的最大值和最小值等信息。
- Row Data:存的是具体的数据,按列进行存储,并对每个列进行编码,分成多个Stream来存储。
- Stripe Footer:存放的是各个Stream的位置以及各column的编码信息。
- Tail由File Footer和PostScript组成。File Footer中保存了各Stripe的其实位置、索引长度、数据长度等信息,各Column的统计信息等;PostScript记录了整个文件的压缩类型以及File Footer的长度信息等。
- 在读取ORC文件时,会先从最后一个字节读取PostScript长度,进而读取到PostScript,从里面解析到File Footer长度,进而读取FileFooter,从中解析到各个Stripe信息,再读各个Stripe,即从后往前读。
如果我想读取a列的内容,则读取逻辑是什么样的?
- 首先要先读取整个文件的最后一个字节,这样可以拿到File Footer的长度,之后可以往前推,拿到整个File Footer的内容,之后就可以从File Footer当中拿到有关strip的信息。先拿到索引长度,通过索引长度定位数据位置。并且Strip Footer的信息也需要拿到,这样才能知道数据按什么格式去进行编码的,最后对拿到的列进行解析即可。
- 文件格式对于加快hive读数据的速度是有很大的帮助的。
如果现在我有一张表,我希望其底层采用ORC的存储格式,我要如何做?
create table orc_table
(column_specs)
stored as orc
tblproperties (property_name=property_value, ...);
ORC文件格式支持的参数如下:
参数 | 默认值 | 说明 |
---|---|---|
orc.compress | ZLIB | 压缩格式,可选项:NONE、ZLIB,、SNAPPY |
orc.compress.size | 262,144 | 每个压缩块的大小(ORC文件是分块压缩的) |
orc.stripe.size | 67,108,864 | 每个stripe的大小 |
orc.row.index.stride | 10,000 | 索引步长(每隔多少行数据建一条索引) |
- ORC压缩是不支持切片的,但压缩也不是整个文件进行压缩的,是分块压缩的。
- 每张表的具体写法都是如下图所示,都会包含SERDE、INPUTFORMAT、OUTPUTFORMAT的
1、ROW FORMAT SERDE:用来序列化和解序列化一行数据的。
2、INPUTFORMAT:写文件的
3、OUTPUTFORMAT:读文件的
(3)Parquet
Parquet文件是Hadoop生态中的一个通用的文件格式,它也是一个列式存储的文件格式。
Parquet文件的格式如下图所示:
- 如果我要读取a列数据,则会先跳过最后4个字节找到footer长度,在根据footer的内容找到每个column chunk的元数据信息,之后就可以根据这些信息,快速定位到其想要的列。
一个Parquet文件的基本结构,文件的首尾都是该文件的Magic Code,用于校验它是否是一个Parquet文件。
首尾中间由若干个Row Group和一个Footer(File Meta Data)组成。
每个Row Group包含多个Column Chunk,每个Column Chunk包含多个Page。以下是Row Group、Column Chunk和Page三个概念的说明:
- 行组(Row Group):一个行组对应逻辑表中的若干行。
- 列块(Column Chunk):一个行组中的一列保存在一个列块中。
- 页(Page):一个列块的数据会划分为若干个页。
- Footer(File Meta Data)中存储了每个行组(Row Group)中的每个列快(Column Chunk)的元数据信息,元数据信息包含了该列的数据类型、该列的编码方式、该类的Data Page位置等信息。
Create table parquet_table
(column_specs)
stored as parquet
tblproperties (property_name=property_value, ...);
支持的参数如下:
参数 | 默认值 | 说明 |
---|---|---|
parquet.compression | uncompressed | 压缩格式,可选项:uncompressed,snappy,gzip,lzo,brotli,lz4 |
parquet.block.size | 134217728 | 行组大小,通常与HDFS块大小保持一致 |
parquet.page.size | 1048576 | 页大小 |
3.3 压缩
在Hive表中 [即表里面存储的数据] 和计算过程 [比如map结束之后,往reduce里面发送数据的时候,数据是否需要压缩] 中,保持数据的压缩,对磁盘空间的有效利用和提高查询性能都是十分有益的。
(1)Hive表数据进行压缩
在Hive中,不同文件类型的表,声明数据压缩的方式是不同的。
1)TextFile
若一张表的文件类型为TextFile,若需要对该表中的数据进行压缩,多数情况下,无需在建表语句做出声明。直接将压缩后的文件导入到该表即可,Hive在查询表中数据时,可自动识别其压缩格式,进行解压。
需要注意的是,在执行往表中导入数据的SQL语句时,用户需设置以下参数,来保证写入表中的数据是被压缩的。
- 直接往文本文件当中写数据,则hive是不会进行压缩的,因此往一张文本文件的表里面写数据,需要自己去声明压缩。
- 情况一:如果采用load的方式往文本文件里面导入数据,则需要保证load的文件是压缩好的文件。
- 情况二:如果是往一张表里面去insert数据的话,需要通过配置参数保证数据是压缩的。
--SQL语句的最终输出结果是否压缩
set hive.exec.compress.output=true;
--输出结果的压缩格式(以下示例为snappy)
set mapreduce.output.fileoutputformat.compress.codec =org.apache.hadoop.io.compress.SnappyCodec;
2)ORC
若一张表的文件类型为ORC,若需要对该表数据进行压缩,需在建表语句中声明压缩格式如下:
create table orc_table
(column_specs)
stored as orc
tblproperties ("orc.compress"="snappy");
- 通过声明参数之后,只要往这张表里面去insert数据,就会自动进行压缩的。
- 同样的,当去读取表当中的数据之后,也能够自动识别压缩算法并且进行解压。
3)Parquet
若一张表的文件类型为Parquet,若需要对该表数据进行压缩,需在建表语句中声明压缩格式如下:
create table orc_table
(column_specs)
stored as parquet
tblproperties ("parquet.compression"="snappy");
(2)计算过程中使用压缩
计算过程有包括两种场景:
1、是一个MR的中间结果进行压缩。mapper结束之后,会把数据写入到hdfs,之后reducer读取数据的时候,这个中间阶段是否要进行压缩。
2、
1)单个MR的中间结果进行压缩
单个MR的中间结果是指Mapper输出的数据,对其进行压缩可降低shuffle阶段的网络IO,可通过以下参数进行配置:
--开启MapReduce中间数据压缩功能
set mapreduce.map.output.compress=true;
--设置MapReduce中间数据数据的压缩方式(以下示例为snappy)
set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
2)单条SQL语句的中间结果进行压缩
单条SQL语句的中间结果是指,两个MR(一条SQL语句可能需要通过MR进行计算)之间的临时数据,可通过以下参数进行配置:
--是否对两个MR之间的临时数据进行压缩
set hive.exec.compress.intermediate=true;
--压缩格式(以下示例为snappy)
set hive.intermediate.compression.codec= org.apache.hadoop.io.compress.SnappyCodec;