hive笔记
Hive笔记
1. Hive是什么?
一个SQL的解析引擎,将SQL转译成MR,本质还是MR,但是并不是所有任务都是MR
比如:select某一些字段,通过limit取出前几行,只需要把文件从上往下读top几行就可以了,不需要单独启动一个MR
什么情况下会转MR?
一般来说任务比较复杂,需要做一些shuffle。
比如 join
2. hive的本质
Hive中的表来描述数据的结构(元数据),只有表的定义,数据实际存储在HDFS上
3. hive在工作中?
Hive内容是 读多写少 。所以不支持对数据的改写和删除。
update等
4. HiveSQL和传统SQL?
HiveSQL和传统SQL是有区别的
可扩展性:
UDF: 用户自定义普通函数,1对1,常用于select语句 ,对查询结构做格式化处理
UDAF: 用户自定义聚合函数,多对1, 需要group by联合使用
UDTF: 用户自定义表生成函数,1对多, 分词
延迟高:MapReduce计算框架
数据规模大:数据存在于HDFS
数据检查:
HQL:读时模式:
只有读数据的时候,hive才会检查(数据字段解析、schema),检查数据是否规范
-优点:加载(load)数据非常迅速,因为不需要对数据做解析,仅仅是对文件的复制和移动
SQL:写时模式:
写的时候做数据处理(为后续查询性能,建立索引、压缩)
-优点:读取的时候速度快,因为读之前已经对数据处理过了,读的时候没有必要花费额外的时间去对数据做进一步的检查
-缺点:加载数据会花费很多时间
针对大数据来说,读时模式比较多
5. Hive的系统架构
1) 用户接口cli(终端、UI、JDBC)
2) Driver语句转换,将CMD编译成MR,Driver是整个Hive的核心
3) 数据存储:实际数据HDFS+元数据,元数据存放:
1> 默认derby:单用户模式
2> mysql:多用户模式(本地+远程)
*******>>>> 尽可能将元数据和真实数据分别存放与不同的位置 <<<<**********
6. 数据管理(4种数据类型)
创建过程和数据加载过程,这两个过程是分别独立完成的,也可以在同一个语句中完成
1) Table:内部表
数据加载过程中,实际的数据会被移动到数据仓库目录中,之后对数据的访问直接在数据仓库的目录中进行。
删除内部表:表中数据和元数据同时被删除(硬链接)
2) External Table:外部表
删除外部表:元数据被删除,但是实际数据仍然存在(软连接)
更安全,数据不完全依赖 Hive 自身管理
数据加载过程中,实际的数据不会被移动到数据仓库目录中
#desc formatted ${tableName};
查看表结构的详细信息
表的信息::: => Table Type:表的类型
3) Partition:辅助查询,缩小查询范围,加快数据检索速度
不是所有的数据字段都适合做partition
4) Bucket:
1> 控制reduce输出文件数量,类似reduce操作(设置reduce的个数 mapred.reduce.tasks=${num} )
2> 采样{ TABLESAMPLE(BUCKET x OUT OF y)}
tablesample是采样语句。语法如上。 y 必须是table总bucket数的倍数或者因子。
1. hive 根据 y 的大小,决定采样的比例。
2. x 表示从哪个bucket开始抽取。
hive中table可以拆分成partition,table和partition可以通过‘CLUSTERED BY ’进一步分bucket,bucket中的数据可以通过‘SORT BY’排序。
例如, table总共分了32份bucket,当 tablesample(bucket 3 out of 16),表示总共抽取了(32/16=)2 个bucket的数据,分别为第3个bucket和第(3+16=)19 个bucket的数据。
Hive的表本质就是Hadoop的目录/文件
- hive默认表存放路径一般都是在工作目录的hive目录里面,按表名做文件夹分开,如果有分区表的话。分区值是子文件夹,可以直接在其他 M/R job 里直接应用这部分数据
问题1:内部表和外部表怎么区分?
desc formatted ${TableName};
Table Type字段:判断是内部表或者外部表
问题2:partition怎么处理?
不是所有的数据字段都适合做partition , partition的条件会成为目录的一部分,如果一个字段取值特别多
如果按照这个字段去做partition,会产生大量的数据碎片
所以适合做partition的字段,要求它们的数据内容是可以穷举出来的
比如:时间,类型
7. 优化
mapred.map.tasks=${num}(实际运行的并发度并不是按照这个数字来分配,只是作为一个参考)
1) map优化——调整block大小——>并发度调整
set mapred.max.split.size=${num};//每个map处理最大文件大小,单位是Byte,确认启动多少map数量
set mapred.min.split.per.node=${num};//节点中可以处理最小的文件大小
set mapred.min.split.per.rack=${num};//机架中可以处理最小的文件大小
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
在开启了org.apache.hadoop.hive.ql.io.CombineHiveInputFormat后,一个data
node节点上多个小文件会进行合并,合并文件数由mapred.max.split.size限制的大小决定。
mapreduce——combiners优化(预处理,在map的阶段先对reduce做一个提前的聚合)
set hive.map.aggr=${true/false} //combiners的功能开启
2) reduce优化
//hive.exec.reducers.bytes.per.reducer;//reduce任务处理的数据量
mapred.reduce.tasks=${num}//设置reduce书里的数据量
//mapred.reduce.tasks=${num}优先级高
reduce优化后影响文件个数,因为每个reduce代表一个输出文件
3) mapreduce那些情况下会产生1个reduce的现象:
时间上,会对数据的产生,有很大的影响。需要把所有的数据全部聚集到一个reduce节点上。
//怎么样避免只有一个reduce的情况?
1. 没有group by —— 有聚合函数的情况下,如果没有group by的话,会只产生一个reduce
2. order by —— 会产生一个reduce(全局排序)
优化方法:用distribute by和sort by来替代
(1) sort by:不是全局排序。保证每个reduce内部是排序的,但不保证全局是有序的
(2) distribute by:控制map端如何拆分数据给reduce,可以把它想象成partition(分桶)
例子: select * from ${TableName} distribute by ${字段} sort by ${列} desc;
//desc降序
(3) cluster by:相当于把sort by和distribute by结合起来了,默认只能升序
特殊形式:
例子: select * from ${TableName} distribute by $a sort by $a;
改写为:select * from ${TableName} cluster by $a;
如果用cluster by,dustribute by和sort by的字段必须一致。
3. 笛卡尔积:join的时候不加on条件或者无效的on条件,Hive只能使用一个reduce来完成笛卡尔积
4) 加快查询速度
1. 分区裁剪:partition
2. 笛卡尔积:join要带on
3. MAP-JOIN:指定小表,内存处理,通常不超过1G或者50W记录
MAPJOIN会把小表全部读入内存中,在map阶段直接拿起另一个表的数据和内存中表数据做匹配
由于在map阶段进行了join操作,省去了reduce运行,效率也会高很多。
4. union all: union all(不去重)和 union(去重) => union 比 union all 花费的性能大
5. mulit insert & multi group by
/* multi group by 可以将查询中的多个group
by操作组装到一个MapReduce任务中,起到优化作用 */
6. automatic merge:对多个小文件进行合并
hive.merge.mapfiles = true? 是否合并Map输出文件,默认为true
hive.merge.mapredfiles = false? 是否合并Reduce文件,默认为false
hive.merge.size.per.task = 256*1000*1000? 合并文件的大小
//使用的时候前面加 set
7. Multi-Count Distinct 负载均衡(减缓数据倾斜)
set hive.groupby.skewindata = true;
指定大表:
/*+STREAMTABLE( ${大表} )*/
指定小表:
/*+MAPJOIB( ${小表} ) */
不指定任何大表和小表的话,hive认为最后一个表为大表
使用multi group by 之前必须配置参数:
hive.multigroupby.singlemr
true
5) join操作
#
(1) 语句优化:
1. 多表连接:如果join中,多个表的join key为同一个,则join会转换为单个 MapReduce 任务
2. 表的连接顺序:
(2) 条件判断角度:避免join过程中出现大量结果,尽量在on中完成所有条件判断
左连接时,左表中出现的join字段都保留,右表没有连接上的都为空。
执行书序是,首先完成2表join,然后再通过where条件进行过滤,这样在join过程中可能会输出大量结果,再对这些结果进行过滤,比较耗时。可以进行优化,将where条件放在on后,在join的过程中,就对不满足条件的记录进行与预先过滤。
#
(3) 并行执行:set hive.exec.parallel=true
——同步执行hive的多个阶段,hive在执行过程,讲一个查询转化成一个或者多个阶段。某个特定的
job可能包含众多的阶段,而这些阶段可能并非完全相互依赖的,也就是说可以并行执行的,这样可
能使得整个job的执行时间的缩短。
主要目标:解决数据倾斜问题:
总结:
1. 大表和小表之间的关联
2. 大表和大表之间的关联
Hive的优化——数据倾斜
操作:Join,Group by,Count Distinct
原因:key分布不均,人为的建表疏忽,业务数据特点
症状:1. 任务进度长时间维持在99%(或者100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。
2. 查看未完成的子任务,可以看到本地读写数据量积累非常大,通常超过10GB可以认定为发生数据倾斜。
倾斜度:1. 平均记录数超过50W且最大记录数是超过平均记录数的4倍。
2. 最长时长比平均时长超过4分钟,且最大时长超过平均时长的2倍。
万能方法:hive.groupby.skewindata=true
数据倾斜——大小表关联
原因:Hive在进行join时,按照join的key进行分发,而在join左边的表的数据会首先读入内存,如果左边表的key相对分散,读入内存的数据会比较小,join任务执行会比较快;而如果左边的表key比较集中,而这张表的数据量很大,那么数据倾斜就会比较严重。而如果这张表时效表,则还是应该把这张表放在join左边。
思路:1. 将key相对分散,并且数据量小的表放在join的左边,这样可以有效减少内存溢出错误发生的几率
2. 使用map join让小表先进内存。
方法:Smalltable join Bigtable
数据倾斜——大大表关联
原因:日志中有一部分的user_id是空或者0的情况下,导致再用userid进行hash分桶的时候,会将日志中userid为0或者空的数据分到一起,导致了过大的倾斜。
思路:把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后并不影响最终结果
方法:on case when(x.uid='-' or x.uid='0'or x.uid is null) then concat('dp_hive_search',rand()) else x.uid end=f.user_id;
[特殊]:数据倾斜——大大表关联 (业务削减)
案例:Select * from dwlog t join dwuser t1 on t.userid=t1.userid
现象:两个表都上千万,跑起来很悬
*思路:当天登录的用户比较少
方法: ``` Select/+MAPJOIN(t12)/ * from dwlog t11 join ( select/+MAPJOIN(t)/ t1. from ( select userid from dwlog group by userid ) t join dw_user t1 on t.userid=t1.userid ) t12 on t11.userid=t12.userid
数据倾斜——聚合时存在大量特殊值
原因:做count distinct时,该字段存在大量值为null或空的记录。
思路:1. count distinct时,将值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结果中加1。
2. 如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union。
方法: ``` select cast(count(distinct(user_id))+1 as bigint) as usercnt
from taba
where userid is not null and userid <> ''
数据倾斜——空间换时间
案例:Select day,count(distinct sessionid),count(distinct userid) from log a group by day
问题:同一个reduce上进行distinct操作时压力很大。
方法: ``` select day, count(case when type='session' then 1 else null end) as sessioncnt, count(case when type='user' then 1 else null end) as usercnt from ( select day,sessionid,type from ( select day,sessionid,'session' as type from log union all select day userid,'user' as type from log ) group by day,sessionid,type ) t1 group by day