Bootstrap

离线和实时大数据开发实战 笔记三

hive 

分区:分区在创建表的时候使用 PARTITIONED BY从句定义 CREATE TABLE logs (ts BIGINT , line STRING)  PARTITIONED BY (dt STR NG,country STRING); 以 dt和country分区 

分桶:在表或者分区中使用桶通常有两个原因:是为了高效查询,桶在表中加入了特殊的结构, Hive 在查询的时候可以利用这些结构提高效率 例如,如果两个表根据相同的字段进行分桶,则在对这两个表进行关联的时候,可以使用 map-side 关联高效实现,前提是关联的字段在分桶字段中出现。二是可以高效地进行抽样 在分析大数据集时,经常需要对部分抽样数据进行观察和分析,分桶有利于高效实现抽样.CREATE TABLE bucketed users(id INT, name STRING) CLUSTERED BY (id) INTO 4 BUCKETS;指定表根据 id 字段进行分桶,并且分为4个桶.分桶时, Hive 根据字段哈希后取余数来决定数据应该放在哪个桶,因此每个桶都是整体数据的随机抽样.在map-side 的关联中,两个表根据相同的字段进行分桶,因此处理左边表的 bucket时,可以直接从外表对应的 bucket 中提取数据进行关联操作 map-side 关联的两个表不定需要完全相同 bucket 数量,只要成倍数即可。
需要注意的是, Hive 并不会对数据是否满足表定义中的分桶进行校验,只有在查询时出现异常才会报错 因此,一种更好的方式是将分桶的工作交给 Hive 来完成(设 hive.enforce. bucketing)属性为 true 即可.

hive在join时应该将最大的表放在最后,因为每次MapReduce任务的逻辑是,Reduce会缓存join序列中除最后一张表外的所有表记录,再通过最后一张表将结果序列化到文件系统。

如果想限制 join 的输出,应该在 WHERE 子句中写过滤条件,或是在 join 子句写,但是表分区的情况很容易混淆,比如下面的第一个 SQL 语句所示,如果d表中找不到对应c表的记录,d 表的所有列都会列出 NULL ,包括 ds列。也就是说, join 会过滤d表中
不能找到匹配c表 join key 的所有记录这样, LEFT OUTER 就使得查询结果与 WHERE子句无关,解决办法是在 join 时指定分区(如下面的第二个 QL 语句所示):
// 第一个 SQL 语句
SELECT c.val, d.val FROM c LEFT OUTER JOIN d ON (c.key=d.key) 
WHERE a.ds='2010-07-07' AND b.ds='2010-07-07 ‘ 
// 第二个 SQL 语句
SELECT c.val, d.val FROM c LEFT OUTER JO d 
ON (c.key=d.key AND d.ds=’ 2009-07-07 ’ AND c.ds='2009-07-07')   这里的优化自我感觉是谓词下推,先通过选择分区将数据拿到然后在进行join判断。

 LEFT SEMI JOIN IN/EXISTS 查询的一种更高效的实现。其限制是: JOIN子句中右边的表只能在ON子句中设置过滤条件,在 WHERE子句SELECT子句或其他方过滤都不行。

select语句执行图解

select order _id, buyer_id,cate_name from orders_table where day=' 20170101' and cate_name=’ iphone7’  单纯的只是查询过滤也就只有Map 通过split切割然后启动对应个数的Map任务。

group by 语句执行图解

Select city,count(oder id) as iphone7 count from orders table where day='20170101' and cate name='iphone7'group by city;

1)分割文件确定Map数  
2)map读取文件 调用Map函数,检查检查其商品类目是否为 Phone7,如果是,则输出形如<city,1>的键值对 
3)数据溢写也就是分割(有Combiner则执行)  
4)文件合并将溢写产生的小文件合并(有Combiner则执行) 一般每个Map都会产生Reduce数量的文件  通过CityId hash取模确定唯一的Reduce 
5)Reduce端copy Map参数的文件 
6)合并Map产生的文件 
7)输出合并好的文件 Reduce Task的输入准备完毕    执行Reduce函数 也就是count聚合   将结果输出到本地  
8)Hadoop 合并 Reduce Task 任务的输出文件到输出目录                                                                                                                          

join语句执行图解

Select tl.order_id,tl.buyer_id,t2.age 
From 
(select order id,buyer id 
from orders table 
where day=’ 20170101' and cate_name=’ iphone7' ) t1
Join 
(select buyer_id,age from buyer_table where buyer_status =' 有效) t2 
On tl.buyer_id=t2.buyer_id ;

t1和t2的过程和之前的一样Select tl.order_id,tl.buyer_id,t2.age From t1 Join  t2 On tl.buyer_id=t2.buyer_id ; Map过程和之前是一样的,Shuffle阶段分区依据变为根据buyer_id取hash 值然后%reduce的个数,确保相同的buyer_id发送到同一个reduce上。Reduce阶段,是根据 join 键值将其他列关联进来的过程,根据 buyer_id 将它们的值关联合并成一行,并写入本地的输出文件中。在此需要注意的是,如果 buyer 表中买家存在重复,那么 Reduce 任务的输出也会存在。                                                                             
如果是三表join join条件相同则只会有一次reduce操作,如果join条件不一致,则有两次reduce操作。 其他join依此类推。

   join 无关优化

1.group by引起的倾斜优化

设置下面的参数:                                                                                                                                                                                                    set hive.map.aggr = true 
set hive.groupby.skewindata=true                                                                                                                                                                     此时 Hive 在数据倾斜的时候会进行负载均衡,生成的查询计划有两个MapReduce  Job。第一个MapReduce Job中,Map 的输出结果集合会随机分布到 Reduce 中,每个Reduce 做部分聚合操作并输出结果,这样处理的结果是相同的 GroupBy Key 有可能被分布到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MapReduce Job 再根据预处理的数据结果按照 GroupBy Key 分布到 Reduce 中(这个过程可以保证相同的 GroupBy Key布到同 Reduce 中),最后完成最终的聚合操作。其实本质就是将一个MapReduce分为多个MapReduce。

2.count distinct 优化

select count(distinct user) from some_table;                                                                                                                                                    由于必须去重,因此 Hive 将会把 Map 阶段的输出全部分布到一个 Reduce Task 上,此时很容易引起性能问题 对于这种情况,可以通过先 group by 再count 的方式来优化,化后的 SQL 如下:                                                                                                           select count(*)  from (select user from some table group by user ) tmp; 
其原理为:利用 group by 去重,再统计 group by 的行数目

join相关优化

大表 join 小表优化

首先介绍大表 join 小表优化。仍以销售明细事实表为例来说明大表 join 小表的场景。
假如供应商会进行评级,比如(五星、四星、 两星、 星),此时业务人员希望能够分析各供应商星级的每天销售情况及其占比。
select 
       Seller_star 
          ,count(order id) as order cnt 
from 
(       Select order_id,seller_id 
           from dwd_sls_fact_detail_table 
          where partition_value='20170101'
) a 
left outer join
(   Select seller_id, seller_star 
     from dim_seller 
     where partition value=’ 20170101 ’ 
) b 
on a.seller_id=b.seller_id   group by b.seller_star; 
但正如上述所言,现实世界的二八准则将导致订单集中在部分供应商上,而好的供应商的评级通常会更高,此时更加剧了数据倾斜的程度,如果不加以优化,上述 SQL 将会耗费很长时间,甚至运行不出结果。
通常来说,供应商是有限的,比如上千家、上万家,数据量不会很大,而销售明细事实表 比较大,这就是典型的大表 join 小表问题,可以通过 mapjoin 的方式来优化,只需添mapjoin hint 即可 优化后的 SQL 如下:
select /*+mapjoin(b)*/ 
      Seller_star 
          ,count(order id) as order cnt 
from 
(       Select order_id,seller_id 
           from dwd_sls_fact_detail_table 
          where partition_value='20170101'
) a 
left outer join
(   Select seller_id, seller_star 
     from dim_seller 
     where partition value=’ 20170101 ’ 
) b 
on a.seller_id=b.seller_id   group by b.seller_star; 
/*+mapjoin(b)*/即 mapjoin hint ,如果需要 mapjoin 多个表,则格式为 /*+mapjoin (b, c, d)*/ Hive 对于 mapjoin 是默认开启的,设置参数为:Set hi ve.auto.convert.join=ture;
mapjoin 优化是在 Map 阶段进行 join ,而不是像通常那样在 Reduce 阶段按照 join 列进行分发后在每个 Reduce 任务节点上进行 join ,不需要分发也就没有倾斜的问题,相反 Hive会将小表全量复制到每个 Map 任务节点(对于本例是 dim_seller ,当然仅全量复制sql 指定的列),然后每个 Map 任务节点执行 lookup 小表即可。
从上述分析可以看出,小表不能太大,否则全量复 制分发得不偿失, 实际上Hive 根据参数 hive.mapjoin.smalltable.filesize ( 11.0 本后是 hive.auto.convert.join.noconditionaltask.size 来确定小表的大小是否满足条件(默认 25M ),实际中此参数值所允许的最大值可以修改,但是一般最大不能超过 lGB (太大的话 Map 任务所在的节点内会撑爆, Hive会报错 另外需要注意的是, HDFS 显示的文件大小是压缩后的大小, 当实际加载到内存的时候,容量会增大很多,很多场景下可能会膨胀10倍)。实际上也就是全局变量。

大表 join 大表优化

A表为一个汇总表(每个买家共成交了多少单,总金额是多少)。B表为卖家基本信息表。因为在二八原则,一部分卖家会有几百万的买家,这个时候就会有数据倾斜产生。

1.转换为mapjoin   
A join B  分别通过A和B的where先过滤 判断过滤后的A或者B其中是否有符合mapjoin条件的表

2.join 时用 case when 语句
此种解决方案应用场景为:倾斜的值是明确的而且数量很少,比如 null 起的倾斜。其核心是将这些引起倾斜的值随机分发到Reduce 其主要核心逻辑在与join时对这些特殊值concat 随机数 ,从而达到随机分发的目的。此方案的核心逻辑如下:
Select a . user id,a.order id,b.user id 
From table a a 
Join table b b 
On (case when a.user_id is null then concat ( ‘ h i v e ' ,rand()) else a.user_id end)=b.user_id 
Hive已对此进行了优化,只需要设置参数skewinfo和skewjoin 参数,不需要修改SQL 代码 例如,由 table_B 的值 “0”和“1”引起了倾斜,只需作如下设置:
set hive.optimize.skewinfo=table_B: (seller id) [("0")("1")]; 
set hive.optimize.skewjoin=true;

3.倍数B表,再取模 join

因为B表中的一个卖家对应多个买家,这个时候将B表扩大10倍,然后join。例如 1个卖家对应100万个买家 这是时候把同一个卖家变为10个  那么其中每一个对应的卖家也就10万个。这样虽然减少了数据倾斜,但是也扩大了B表数量。

4.基于方案3   

首先将大卖家Id查询出来,然后单独对这些大卖家扩大N倍,这样就减少了整体表的数量。

5.动态一分为二

将大卖家Id查询出来,做成全局变量表,然后大卖家和非大卖家分别join A。
 

hive更多是在这本书上看到的,本身工作做没有用到hive,更多的是纸上谈兵。用户有机会接触到就单独写写hive。

 

 

 

;