hive

1 hive的基本操作

1.1 分区表

创建分区表的好处是查询时,不用全表扫描,查询时只要指定分区,就可查询分区下面的数据。每个分区是一个小目录。

分区表可以是内部表,也可以是外部表。

--创建分区表
CREATE EXTERNAL TABLE student_par(name STRING)
PARTITIONED BY (age int,sex STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
LOCATION '/user/zms/hive/student_par';

--添加分区
alter table student_par add IF NOT EXISTS partition(age=10,sex='boy') location '10/boy';	
alter table student_par add IF NOT EXISTS partition(age=10,sex='girl');     --如果不指定location路径,hive就自动创建一个

--在hive客户端递归查看hdfs目录
dfs -ls -R /user/zms/hive/student_par;

--给分区表添加多个分区
alter table student_par add IF NOT EXISTS partition (age=11,sex='boy') location '11/boy' partition (age=11,sex='girl') location '11/girl';

--删除分区
alter table student_par drop if exists partition(age=11,sex='boy');

--查看student_par表的所有分区
show partitions student_par;

1.2 hive当中的数据加载

1.LOAD加载数据

格式:LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)]

说明:

        1)filepath 可以是目录,但该目录下不能包含子目录。

        2)指定LOCAL就是本地文件上传,如果没有指定LOCAL,则使用hdfs文件 (本地load和hdfs load有什么区别:本地load不会对本地数据文件做任何处理,只是将本地数据拷贝(copy)到表定义的数据目录里;hdfs load是把hdfs数据移动(mv)到表定义的数据目录里,移动后hdfs上的数据就不存在了)。

        3)文件加载hive没有做严格校验,文件格式和压缩选项等匹配需要用户自己保证。

        4)分区表要指定具体加载数据分区。

        5)如果指定OVERWRITE,会覆盖相应表数据或分区数据,相当于删除(rm)原有目录数据,然后上传新数据文件

--先rm原有目录数据,然后上传新数据文件, hdfs 执行mv操作
load data INPATH 'hdfs://ns1/user/hive/data/maxword' overwrite into table ext_task PARTITION (taskname='maxword01');

2.通过SELECT加载数据

格式:

   INSERT OVERWRITE TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...) [IF NOT EXISTS]] select_statement1 FROM from_statement;   --通过select,将select数据覆盖表或分区的语法格式

   INSERT INTO TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1 FROM from_statement;   --通过select,将select数据追加到表或分区的语法格式

说明:

        1)当加了 IF NOT EXISTS,如果存在分区就跳过下面的select语句。

        2)INSERT OVERWRITE 会覆盖表或分区数据,但覆盖分区时增加IF NOT EXISTS,如果分区已经存在则不会覆盖。

        3) INSERT INTO 向表或分区追加数据,不影响历史数据。

--如果分区不存在,则将select的数据覆盖到表分区
insert overwrite table ext_task partition(taskname='wordcount04') IF NOT EXISTS
select word,num from ext_task where taskname='wordcount03';

--通过select,将select数据追加到表分区
insert into table ext_task partition(taskname='wordcount04')
select word,num from ext_task where taskname='wordcount03';

2 hive当中的数据存储格式

是
左边为逻辑表,右边第一个为行式存储,第二个为列式存储

2.1 简单介绍

hive的存储格式分两大类:行存储和列存储。

行存储的特点: 查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。

列存储的特点: 因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性地设计更好的压缩算法。

存储格式主要有四种:text(默认存储格式)、sequenceFile、parquet、orc。其中text、sequenceFile基于行式存储,parquet、orc基于列式存储。

2.2 orc文件的结构及优点

一个orc文件可分为若干个stripe,一个stripe可分为三个部分,分别是Index Data、Row Data、Stripe Footer

index data:索引数据

row  data:真正的数据存储

stripe  footer:stripe的元数据信息

Hive中的ORC(optimized row columnar)不仅仅有着高压缩比,很大程度地节省了存储空间和计算资源,而且在这个基础上还做了许多优化(主要是在文件中存储了一些轻量级的索引数据)。如果使用Hive作为大数据仓库,强烈建议主要使用ORC文件格式作为表的存储格式。主要优点是:

  1. 容量小。
  2. 存在索引,查询时不需要执行mapreduce

但是orc有个缺点,就是不能支持LZO等支持文件分割的压缩算法。orc经常配合snappy压缩,因为Snappy不支持文件分割操作,所以压缩文件「只会被一个任务所读取」,如果该压缩文件很大,那么处理该文件的Map需要花费的时间会远多于读取普通文件的Map时间,这就是常说的「Map读取文件的数据倾斜」。

那么为了避免这种情况的发生,就需要在数据压缩的时候采用bzip2和Zip等支持文件分割的压缩算法。但恰恰ORC不支持刚说到的这些压缩方式,所以这也就成为了大家在可能遇到大文件的情况下不选择ORC的原因,避免数据倾斜。

在Hive on Spark的方式中,也是一样的,Spark作为分布式架构,通常会尝试从多个不同机器上一起读入数据。要实现这种情况,每个工作节点都必须能够找到一条新记录的开端,也就需要该文件可以进行分割,但是有些不可以分割的压缩格式的文件,必须要单个节点来读入所有数据,这就很容易产生性能瓶颈。

「所以在实际生产中,使用Parquet存储,lzo压缩的方式更为常见,这种情况下可以避免由于读取不可分割大文件引发的数据倾斜。 但是,如果数据量并不大(预测不会有超大文件,若干G以上)的情况下,使用ORC存储,snappy压缩的效率还是非常高的。」

2. LZO文件的结构及优点

这里存储模型又可以理解为存储格式或文件格式,Parquet 的存储模型主要由行组(Row Group)、列块(Column Chuck)、页(Page)组成。

1、行组,Row Group:Parquet 在水平方向上将数据划分为行组,默认行组大小与 HDFS Block 块大小对齐,Parquet 保证一个行组会被一个 Mapper 处理。

2、列块,Column Chunk:行组中每一列保存在一个列块中,一个列块具有相同的数据类型,不同的列块可以使用不同的压缩。

3、页,Page:Parquet 是页存储方式,每一个列块包含多个页,一个页是最小的编码的单位,同一列块的不同页可以使用不同的编码方式。

parquet文件对嵌套

另外 Parquet 文件还包含header与footer信息,分别存储文件的校验码与Schema等信息。参考官网的一张图:

preview

lzo压缩格式有很快的压缩/解压速度和合理的压缩率,原本是不支持分片的,但是建了索引后支持分片(split),所以lzo是目前在Hadoop中最流行的压缩格式。hadoop中的lzo不是自带的,如果要支持lzo,需要另外安装。
Hadoop经常用于处理大量的数据,如果期间的输出数据、中间数据能压缩存储,对系统的I/O性能会有提升。综合考虑压缩、解压速度、是否支持split,目前lzo是最好的选择。

是否支持split(是否可切分)是什么意思呢?

官方的说法:对应的压缩算法是否可以搜索数据流的任意位置并进一步往下读取数据。
举例说明,
有一个存储在HDFS文件系统中且不进行压缩的文件,大小为1GB。如果HDFS的块大小设为128M,那么该文件被存储在8个块中。把这个文件作为作为输入数据的MapReduce/Spark作业 将创建8个map任务,每个数据块对应一个任务作为输入数据。
另有一个1G的压缩文件存储在HDFS文件系统中,这个压缩格式不支持split。HDFS仍然将这个文件存储成8个数据块,但是每个单独的map/reduce任务无法独立于其他任务进行处理。这是因为该压缩算法无法从任意数据块进行读取,每个块都是不完整的文件。完整的文件是有首尾标识的,切分后,每个数据块不可能同时有首尾标识,成了不完整的文件,所以就不能多任务并行处理了。

3 hive的调优

3.1 Fetch抓取

Hive中对某些情况的查询可以不必使用MapReduce计算。例如:SELECT * FROM employees;在这种情况下,Hive可以简单地读取employees对应的存储目录下的文件,然后输出查询结果到控制台。

在hive-default.xml.template文件中hive.fetch.task.conversion默认是more,老版本hive默认是minimal,该属性修改为more以后,在全局查找、字段查找、limit查找等都不走mapreduce

3.2 本地模式

如果数据量非常小,即文件大小和数量都小于阈值时,可以自动通过本地模式在单台机器上处理所有的任务,所有的数据都在一个maptask里面处理,都在一个reducetask里面处理,明显缩短处理小数据集的执行时间。

--开启本地mr,默认是false
set hive.exec.mode.local.auto=true; 

--设置local mr的最大输入数据量,当输入数据量小于这个值时采用local mr的方式,默认为134217728,即128M
set hive.exec.mode.local.auto.inputbytes.max=51234560;    -- 58M

--设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式,默认为4
set hive.exec.mode.local.auto.input.files.max=10;

3.3 hive的数据倾斜

3.3.1 数据倾斜的原因

key分布不均匀。默认情况下,Map阶段同一Key数据分发给一个reduce,当一个key数据过大时就倾斜了。

3.3.2 数据倾斜的表现

任务进度长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。因为其处理的数据量和其他reduce差异过大。

单一reduce的记录数与平均记录数差异过大,通常可能达到3倍甚至更多。最长时长远大于平均时长。

3.3.3 数据倾斜的解决方案

1.大小表join:mapjoin

普通的join是在map端读取两表数据,shuffle传输两表数据,reduce端进行join。在这里,Shuffle 阶段代价非常昂贵,因为它需要排序和合并。减少 Shuffle 和 Reduce 阶段可以提高任务性能。

map端的join应用于大表和小表join(联合)。mapjoin甚至不需要reduce,因为它把小表刷入内存,然后在map阶段就和大表进行join,大大提高了运行速度。

hive.auto.convert.join=true             默认是true,自动将小表刷入内存

hive.ignore.mapjoin.hint=true         默认是true,忽略MAPJOIN标记

如果做A inner join B,系统自动把小表放入内存;

如果做外连接,就手动mapjoin

hive.auto.convert.join=false

hive.ignore.mapjoin.hint=false

如果做A left join B,就只能把B表放入内存(/*+ MAPJOIN(B) */);如果做A right join B,就只能把A表放入内存(/*+ MAPJOIN(A) */)

select /*+ MAPJOIN(A) */ A.country,B.name from A right join B on A.country=B.code;

2.大表join大表:某字段的非法数据(0值或空值)太多会造成数据倾斜

如果null值不要,可以通过where条件筛选掉,定义格式:where 该字段 is not null

如果保留null值,可以将其转化为随机数+字符串,定义格式:when 该字段 is null then concat(rand(),字符串) else 该字段

3.count distinct的字段中包含大量的相同特殊值

数据量小的时候无所谓。

数据量大的情况下,由于每一个key的COUNT DISTINCT操作需要用一个Reduce Task来完成,这一个Reduce需要处理的数据量太大,就会导致整个Job很难完成。

--统计去重字段,大多数重复字段为null

--方法一
select count(distinct c1)+1 from tt where c1 is not null;
--方法二
select sum(1)+1 from tt where c1 is not null group by c1;
--方法三
select count(distinct c1) from tt where c1 is not nul
union
select count(distinct c1) from tt where c1 is nul;

4. group by维度过小,数据过于集中

   数据自身倾斜,比如:北京的用户比其它地方的用户多很多

set hive.map.aggr=true;                      开启map端combiner

set hive.groupby.skewindata=true;     开启负载均衡。当选项设定为 true,生成的查询计划会有两个MR Job第一个MR Job中,Map的输出结果会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是,相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MR Job再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。

3.4 开启严格模式

set hive.mapred.mode=strict;如果sql语句不够优化就报错

设置这个参数,可以限制以下情况:

3.4.1 笛卡尔积

尽量避免笛卡尔积,即避免join的时候不加on条件,或者无效的on条件,Hive只能使用1个reducer来完成笛卡尔积。

尽量多用on少用where来过滤数据,因为on是在关联前过滤数据,where是在关联后过滤数据。过滤数据除了用on,还可以用子查询。

3.4.2 partition表使用时不加分区

查询分区表要进行分区剪裁,即用where中包含分区字段过滤条件来限制查询范围

在执行分区表查询语句的时候 , 如果没有进行分区剪裁 , Hive就不允许执行该查询 , 即不允许用户扫描所有的分区来查询数据。因为一般分区表都会有非常大的数据量 , 而且数据增速也很快 , 如果没有进行分区限制的查询 , 可能会消耗很大的资源来处理这个表。

3.4.3 order by全局排序的时候不加limit

使用order by 时必须要有limit语句来限制 , 因为全排序会将结果分发到一个reduce来处理 , 会消耗大量的时间

3.5 动态分区

Hive默认是静态分区,我们在插入数据的时候要手动设置分区,如果源数据量很大的时候,那么针对一个分区就要写一个insert,比如说,我们有很多日志数据要按日期作为分区字段,在插入数据的时候手动去添加分区,那样太麻烦。因此,Hive提供了动态分区,动态分区简化了我们插入数据时的繁琐操作。

--设置参数动态分区

--开启动态分区
set hive.exec.dynamic.partition=true;

--这个属性默认是strict,即限制模式,strict是避免全分区字段是动态的,必须至少一个分区字段是指定有值即静态的,且必须放在最前面。设置为nonstrict之后所有的分区都可以是动态的了。
set hive.exec.dynamic.partition.mode=nonstrict;

--表示每个节点生成动态分区的最大个数,默认是100
set hive.exec.max.dynamic.partitions.pernode=10000;
  
--表示一个DML操作可以创建的最大动态分区数,默认是1000
set hive.exec.max.dynamic.partitions=100000;

--表示一个DML操作可以创建的最大文件数,默认是100000
set hive.exec.max.created.files=150000

--将select数据,覆盖到表的动态分区。建议不将表的所有字段做全分区字段,建议指明分区名,然后再插入数据。
insert overwrite table table1 partition (ds, hr)
select key, value, ds, hr FROM table2 WHERE ds is not null;

3.6 使用分桶表

桶是比表或分区更为细粒度的数据范围划分。针对某一列进行桶的组织,对列值哈希,然后除以桶的个数求余,决定将该条记录存放到哪个桶中。桶表就是对一次进入表的数据进行文件级别的划分。

  • 最大限度地保证了每一个桶中文件中的数据量大致相同,不会造成数据倾斜
  • 获得更高的查询处理效果
  • 抽样调查

3.7 控制map个数以及reduce个数

map数是由输入目录里的文件数目和文件大小决定,如果文件大于块大小(128M),就会被拆分,如果小于块大小,则把该文件当成一个块。有多少块就有多少map数。

减少map数

如果一个任务有很多小文件(远远小于块大小128M),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,并行的map数是受限的。

可以通过配置hive参数来自动合并小文件。

增加map数

如果总文件太大,或者文件虽然小但处理逻辑太复杂,就需要增加map数。

可以通过配置hive来自动拆分文件,增加map数。

reduce数

过多地启动和初始化reduce也会消耗时间和资源;另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,这些小文件作为下一个任务的输入,也会出现小文件过多的问题;

在设置reduce个数的时候也需要考虑这两个原则:处理大数据量利用合适的reduce数;使单个reduce任务处理数据量大小要合适;

3.8 开启并行执行

有时候,hive会将一个查询转换成多个阶段,并且某些阶段并非完全互相依赖。这个时候可以使用并行执行模式,让这些阶段同时进行,提高Job处理效率。

3.9 开启JVM重用

JVM重用是Hadoop调优参数的内容,其对Hive的性能具有非常大的影响,特别是对于很难避免小文件的场景或task特别多的场景,这类场景大多数执行时间都很短。

Hadoop的默认配置通常是使用派生JVM来执行map和Reduce任务的。这时JVM的启动过程可能会造成相当大的开销,尤其是执行的job包含有成百上千task任务的情况。JVM重用可以使得JVM实例在同一个job中重新使用N次,每次执行完了maptask或者reducetask之后,不要释放资源,继续给下一个maptask或者reduetask执行。mapreduce或hive可以配置JVM重用次数,通常在10-20之间,具体多少需要根据具体业务场景测试得出。

3.10 关闭推测执行

参见《MapReduce的推测执行(Hive优化)

3.11 数据压缩

开启map输出阶段压缩可以减少job中map task和Reduce task间的数据传输量,可以配置hive参数来开启。

开启reduce输出阶段压缩,可以压缩输出结果。当Hive将输出写入到表中时,输出内容同样可以进行压缩。通过SELECT加载数据时,如果被输入的表的文件是压缩格式,同时在select的输出过程中开启了输出结果压缩,可以加快数据加载的速度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值