一、HQL如何转化成mapreduce?
- 通过Sql Parse(sql解析器)将sql解析成抽象语法树
- 通过语义分析器, 遍历抽象语法树,抽象出查询块
- 通过逻辑计划编译器,遍历查询块,将其翻译为操作树
- 进行逻辑层优化,对操作树进行优化,合并操作符,合并不需要的ReduceSinkOperator,减少shuffle数据量(达到减少MapReduce Job,减少shuffle数据量的目的)
- (逻辑计划转换成物理计划)-将操作树翻译成Mapreduce
- 优化mapreduce,生成最终的执行计划
二、Hive如何进行权限控制?
自0.10版本可通过元数据控制权限,这种权限控制并不是完全安全的,只能防止用户不做某些不合适的事情。
默认情况不开启,意味着所有用户都是超级管理员。
- 1、在hive-site.xml设置以下两个参数:
<property>
<name>hive.security.authorization.enabled</name>
<value>true</value>
<description>enable or disable the hive client authorization</description>
</property>
<property>
<name>hive.security.authorization.createtable.owner.grants</name>
<value>ALL</value>
<description>the privileges automatically granted to the owner whenever a table gets created. An example like "select,drop" will grant select and drop privilege to the owner of the table</description>
</property>
hive.security.authorization.enabled 是否开启权限验证,默认false。
hive.security.authorization.createtable.owner.grants 表的创建者对表的拥有所有操作权限。
权限名称 | 含义 |
---|---|
ALL | 所有权限 |
ALTER | 允许修改元数据(modify metadata data of object) --表信息数据 |
UPDATE | 允许修改物理数据(modify physical data of object) --实际数据 |
CREATE | 允许进行CREATE操作 |
DROP | 允许进行DROP操作 |
INDEX | 允许建索引(目前还没有实现) |
LOCK | 当出现并发的时候允许用户进行LOCK和UNLOCK操作 |
SELECT | 允许用户进行SELECT操作 |
SHOW_DATABASE | 允许用户查看可用的数据库 |
- 2、Hive授权的核心就是用户(user)、组(group)、角色(role),角色定义权限,赋予给用户和组(用户和组就是linux机器上的用户和组),用户归属于组。
基本命令:
1. 角色的创建和删除
create/drop role rolename
2. 赋予权限给角色
grant select on table tablename to rolename
3. 赋予/回收用户角色(权限)
grank rolename to user username
revoke rolename from user username
4. 查看用户所有角色
show role grant user username
三、内部表和外部表
1、创建表
内部表:建表时默认放在数据仓库指向的路径
外部表:建表时呗external修饰,存储位置由自己指定,不指定则存储在数仓指向的路径。
2、管理
内部表:表数据由hive进行管理
外部表:表数据由HDFS进行管理
3、删除表
内部表:删表时,删除元数据和存储数据。
外部表:删表时,只删除元数据,存储在HDFS上的数据不进行删除
4、适合的场景:
内部表:中间表数据或不需要共享的数据
外部表:适用于多部门共享数据
四、hive的优化
(1)不走mapreduce
-
Feach抓取
hive.fetch.task.conversion = more 全局查找、字段查找、limit查找都不走mapreduce
(2)sql的优化
-
列裁剪
只选取需要的列
裁剪对应的参数项为:hive.optimize.cp=true(默认值为真) -
分区裁剪
查询过程中减少不必要的分区
分区参数为:hive.optimize.pruner=true(默认值为真) -
关联字段数据类型要相同
不同数据类型关联产生的倾斜问题,string类型的id和bigint类型的id进行关联碰到倾斜的问题.
把数据类型转换成字符串类型,统一字段数据类型。 -
count(distinct)
一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,使用先GROUP BY再COUNT的方式替换 -
join 操作
-
Map join (小表join 大表)
小表加载进内存,在map端进行join ,避免reduce处理(不符合map join条件的会转换成common join ,在reduce端完成join) hive.auto.convert.join = true hive.mapjoin.smalltable.filesize = 25000000 大表、小表的阈值设置(默认25M以下为小表)
-
大表join大表
(1) 空key过滤
进行表关联前,对空key进行过滤。(2) 空key转换
某个key的空数据过多,但并非异常数据,可对空key赋随机值,使数据均匀地分布到不同的reduce上。
on case when n.id is null then concat(‘hive’,rand()) else n.id = o.id(3) group by操作
默认情况下,Map阶段同一Key数据分发给一个reduce,当一个key数据过大时就倾斜了。- 在map端进行部分聚合
hive.map.aggr = true
hive.groupby.mapaggr,checkinterval = 100000
设置在map端进行聚合操作的条目数目 - 有数据倾斜时进行负载均衡
hive.groupby,skewindata = true
生成的查询计划会产生两个mapreduce,第一个mr job map端输出的结果随机分发到reduce中,每个reduce做部分聚合,并输出结果,此时相同的key可能分发到不同的reduce中,达到负载均衡的目的;第二个map reduce job 基于第一个mr job 的预处理的结果,按照group by key 将数据分布到reduce,进行最终的聚合。
- 在map端进行部分聚合
-
LEFT SEMI JOIN 是 IN/EXISTS 子查询的一种更高效的实现。
LEFT SEMI JOIN 的限制是, JOIN 子句中右边的表只能在 ON 子句中设置过滤条件,在 WHERE 子句、SELECT 子句或其他地方过滤都不行-
left semi join 的限制是, JOIN 子句中右边的表只能在 ON 子句中设置过滤条件,在 WHERE 子句、SELECT 子句或其他地方过滤都不行。
-
left semi join 是只传递表的 join key 给 map 阶段,因此left semi join 中最后 select 的结果只许出现左表。
-
因为 left semi join 是 in(keySet) 的关系,遇到右表重复记录,左表会跳过,而 join 则会一直遍历。这就导致右表有重复值得情况下 left semi join 只产生一条,join 会产生多条,也会导致 left semi join 的性能更高。
-
-
排序
order by 全局排序,只会产生一个reduce
sort by 单机排序,单个reduce结果有序
distribute by + sort by:分区排序,相同一字段值分发到一个reduce,并进行排序。
cluster by :当distribute by 和sort by的字段相同时使用,只能升序排序
(3) 合理设置map数和reduce数
- map数
map数的决定因素: 文件大小、块大小、文件个数
- 小文件过多,合并小文件,减少map数
```bash
合并小文件
set hive.input.format = org.apache.hadoop.hive.ql.io.combineHiveInputFormat;
map only任务结束时合并小文件
set hive.merge,mapfiles = true
mr任务结束时合并小文件
set hive.merge.mapredfiles = true
合并文件大小,默认256M
set hive.merge.size.per.task = 268000000
输出文件平均大小小于该值,启动独立的mr任务进行文件的merge
set hive.merge.smallfiles.avgsize = 16777216
```
-
复杂逻辑增加map数
文件小,但逻辑复杂调整最大切片值,使最大切片值小于块大小,以增加map数,提高任务的执行效率
max(minsize.min(maxsize,blocksize))
set mapreduce.input.fileinputformat.split.maxsize = 100
-
合理设置reduce数
原则:
1、处理大数据量要利用合适的reduce数
2、每个reduce处理的数据量大小要合适Reduce的个数对整个作业的运行性能有很大影响。如果Reduce设置的过大,那么将会产生很多小文件,对NameNode会产生一定的影响,而且整个作业的运行时间未必会减少;如果Reduce设置的过小,那么单个Reduce处理的数据将会加大,很可能会引起OOM异常。
调整reduce数一:
(1)设置每个reduce处理的数据量,默认256M set hive.exec.reducers.bytes.per.reducer = 256000000 (2)设置每个任务最大的reduce数,默认1009 set hive.exec.reducers.max = 1009 (3)计算reduce个数 N=min(参数二,总输入数据量/参数一)
调整reduce数二:
hadoop mapred-default.xml中修改 设置每个job的reduce个数 set mapreduce.job.reducers = 15
很多时候会发现任务中不管数据量多大,不管你有没有调整reduce个数的参数,任务中一直都只有一个reduce任务;其实只有一个reduce任务的情况,除了数据量小于hive.exec.reducers.bytes.per.reducer参数值的情况外,还有以下原因:
- 没有groupby 的汇总
- 使用order by
- 有笛卡尔积
(4)小文件的合并优化
大量小文件,容易在文件存储端造成瓶颈,给HDFS带来压力,影响处理效率。
- 对于已有的小文件
-
使用hadoop archive命令把小文件进行归档。
-
重建表,建表时减少reduce数量。
-
通过参数进行调节,合并Map和Reduce的结果文件来消除这样的影响
用于设置合并的参数有: 是否合并Map输出文件: hive.merge.mapfiles=true(默认值为true) 是否合并Reduce端输出文件: hive.merge.mapredfiles=false(默认值为false) 合并文件的大小:hive.merge.size.per.task=256*1000*1000(默认值为256000000)
- 从源头上控制小文件的产生
小文件的产生:
1.动态分区插入数据,产生大量的小文件,从而导致map数量剧增。2.reduce数量越多,小文件也越多(reduce的个数和输出文件是对应的)。
3.数据源本身就包含大量的小文件。
-
使用Sequencefile作为表存储格式,不要用textfile,在一定程度上可以减少小文件。
-
减少reduce的数量(可以使用参数进行控制)。
-
使用动态分区时,记得按distribute by分区。
(5)存储格式
采用orcfile格式(列式存储)存储数据
行列存储相结合的方式,数据按行分块,块数据进行列式存储,有利于数据压缩和快速存取。
ORC是列式存储,有多种文件压缩方式,并且有着很高的压缩比。
文件是可切分(Split)的。因此,在Hive中使用ORC作为表的文件存储格式,不仅节省HDFS存储资源,查询任务的输入数据量减少,使用的MapTask也就减少了。
提供了多种索引,row group index、bloom filter index。
ORC可以支持复杂的数据结构(比如Map等)
查询的时候不需要扫描全部的数据,而只需要读取每次查询涉及的列,这样可以将I/O消耗降低N倍,另外可以保存每一列的统计信息(min、max、sum等),实现部分的谓词下推。
由于每一列的成员都是同构的,可以针对不同的数据类型使用更高效的数据压缩算法,进一步减小I/O。
由于每一列的成员的同构性,可以使用更加适合CPU pipeline的编码方式,减小CPU的缓存失效。
(6)压缩方式
大数据场景下存储格式压缩格式尤为关键,可以提升计算速度,减少存储空间,降低网络io,磁盘io,所以要选择合适的压缩格式和存储格式,那么首先就了解这些东西。参考该博客
压缩的原因
Hive最终是转为MapReduce程序来执行的,而MapReduce的性能瓶颈在于网络IO和磁盘IO,要解决性能瓶颈,最主要的是减少数据量,对数据进行压缩是个好的方式。压缩虽然是减少了数据量,但是压缩过程要消耗CPU的,但是在Hadoop中,往往性能瓶颈不在于CPU,CPU压力并不大,所以压缩充分利用了比较空闲的CPU。
(7)模式选择
本地模式
对于大多数情况,Hive可以通过本地模式在单台机器上处理所有任务。对于小数据,执行时间可以明显被缩短。
并行模式
Hive会将一个查询转化成一个或多个阶段。这样的阶段可以是MapReduce阶段、抽样阶段、合并阶段、limit阶段。默认情况下,Hive一次只会执行一个阶段,由于job包含多个阶段,而这些阶段并非完全相互依赖,即:这些阶段可以并行执行,可以缩短整个job的执行时间。设置参数,set hive.exec.parallel=true,或者通过配置文件来完成:
set hive.exec.parallel;
严格模式
可以防止用户执行那些可能产生意想不到的影响查询,通过设置Hive.mapred.modestrict来完成。
(8)矢量查询
执行通过一次性批量执行1024行而不是每次单行执行,从而提高扫描、聚合、筛选器和连接等操作的性能。在Hive 0.13中引入,此功能显着提高了查询执行时间,并可通过两个参数设置轻松启用:
设置hive.vectorized.execution.enabled = true;
设置hive.vectorized.execution.reduce.enabled = true;
(9) 分区表
Hive中的分区其实就是分目录,根据某些维度(例如时间等)将数据表分成多份,一个分区表对应HDFS文件系统上一个独立的文件夹,该文件夹下是该分区所有的数据文件;
查询时,通过where表达式选择查询所指定的分区即可,不用再查询整张表的所有数据,提高海量数据查询的效率。
分区类型:
-
静态分区
需要手动指定具体分区 -
动态分区
仅指定字段,系统会自动分区开启静态分区 hive.exec.dynamic.partition = true 设置非严格模式(strick严格模式,必须指定一个静态分区) hive.exec.dynamic.partition.mode = nonstrict 所有执行mr的节点上,最大可以创建多少个动态分区 hive.exec.max.dynamic.partitions = 1000 每个执行mr的节点上,最大可以创建多少个动态分区 hive.exec.max.dynamic.partitions.pernode = 100 整个mr job当中,最大可以创建多少个HDFS文件,默认100000
(10)分桶表
分区提供了一个隔离数据和优化查询的便利方式,但并非所有数据集都能形成合理的分区。对于一张表或分区,hive可以进一步组织成桶(更为细粒度的数据范围划分),分桶针对的是数据文件。
1、分桶规则:
对分桶字段值进行哈希,哈希值除以桶的个数求余,余数决定了该条记录在哪个桶中,也就是余数相同的在一个桶中。
2、创建分桶表并导入数据:
1、创建分桶表
create table tran_order(
account_date string,
cust_name string,
cust_id string,
tran_type string,
sub_tran_type string,
transn string,
orderid string,
tran_amount string,
tran_state string
)
clustered by(transn) into 4 buckets
row format delimited fields terminated by '\t';
2、创建中间表
3、通过查询中间表数据的方式将数据插入到分桶表
insert into tran_order select * from tran_order1;
数据分成了四个文件存储
分桶的实质就是对 分桶的字段做了hash 然后存放到对应文件中,所以说如果原有数据没有按key hash ,需要在插入分桶的时候hash, 也就是说向分桶表中插入数据的时候必然要执行一次MAPREDUCE,这也就是分桶表的数据基本只能通过从结果集查询插入的方式进行导入。
clustered by(col) into n buckets
注意:col字段要是表中存在的字段,建表时指定分几个桶。
需要确保reduce 的数量与表中的bucket 数量一致
1、让hive强制分桶,自动按照分桶表的bucket 进行分桶。(推荐)
set hive.enforce.bucketing = true;
2.手动指定reduce数量
set mapreduce.job.reduces = num;
/
set mapreduce.reduce.tasks = num;
并在 SELECT 后增加CLUSTER BY 语句
3、分桶的优点:
1、提高join查询效率
桶为表加上了额外的结构,Hive 在处理有些查询时能利用这个结构。具体而言,对于JOIN操作两个表有一个相同的列,如果对这两个表都进行了桶操作,可以使用 Map 端连接 (Map-side join)高效的实现,将保存相同列值的桶进行JOIN操作就可以,可以大大减少JOIN的数据量。
2、提高抽样效率
在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。
抽样语句:
tablesample(bucket x out of y)
x:表示从第几桶开始抽数据(1,2,3,4)
y:表示抽数据的比例,是抽数据的分母
比如: 有4个分桶
tablesample(bucket 1 out of 16) 表示从第一桶开始抽数据,抽取第一桶数据的比例为(4(桶数)/16(分母))=1/4,抽取第一桶四分之一的数据
tablesample(bucket 2 out of 32) 表示从第二桶开始抽数据,抽取第二桶数据的比例为(4(桶数)/32(分母))=1/8,抽取第一桶八分之一的数据
分
我们发现其实桶的概念就是MapReduce的分区的概念,两者完全相同。物理上每个桶就是目录里的一个文件,一个作业产生的桶(输出文件)数量和reduce任务个数相同。