参考文章 :
https://www.cnblogs.com/skyEva/p/5632374.html
Hive 优化大纲
大纲地址 : https://blog.csdn.net/u010003835/article/details/105334641
场景六.通过让MAP 端, 多去承担任务, 去减少 Reducer 的计算成本 和 数据传输成本。
1)MAP JOIN 的方式
2) MAP AGGR , 在 Map 端进行预聚合
构建测试数据
use data_warehouse_test;
CREATE TABLE IF NOT EXISTS datacube_salary_org (
company_name STRING COMMENT '公司名称'
,dep_name STRING COMMENT '部门名称'
,user_id BIGINT COMMENT '用户id'
,user_name STRING COMMENT '用户姓名'
,salary DECIMAL(10,2) COMMENT '薪水'
,create_time DATE COMMENT '创建时间'
,update_time DATE COMMENT '修改时间'
)
PARTITIONED BY(
pt STRING COMMENT '数据分区'
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE
;
CREATE TABLE IF NOT EXISTS datacube_salary_basic_aggr(
company_name STRING COMMENT '公司名称'
,dep_name STRING COMMENT '部门名称'
,user_id BIGINT COMMENT '用户id'
,salary DECIMAL(10,2) COMMENT '薪水'
)
STORED AS ORC
;
CREATE TABLE IF NOT EXISTS datacube_salary_dep_aggr(
company_name STRING COMMENT '公司名称'
,dep_name STRING COMMENT '部门名称'
,total_salary DECIMAL(10,2) COMMENT '薪水'
)
STORED AS ORC
;
CREATE TABLE IF NOT EXISTS datacube_salary_company_aggr(
company_name STRING COMMENT '公司名称'
,total_salary DECIMAL(10,2) COMMENT '薪水'
)
STORED AS ORC
;
CREATE TABLE IF NOT EXISTS datacube_salary_total_aggr(
total_salary DECIMAL(10,2) COMMENT '薪水'
)
STORED AS ORC
;
看下每张表的数据
datacube_salary_org 表的数据
+-----------------------------------+-------------------------------+------------------------------+--------------------------------+-----------------------------+----------------------------------+----------------------------------+-------------------------+
| datacube_salary_org.company_name | datacube_salary_org.dep_name | datacube_salary_org.user_id | datacube_salary_org.user_name | datacube_salary_org.salary | datacube_salary_org.create_time | datacube_salary_org.update_time | datacube_salary_org.pt |
+-----------------------------------+-------------------------------+------------------------------+--------------------------------+-----------------------------+----------------------------------+----------------------------------+-------------------------+
| s.zh | engineer | 1 | szh | 28000.00 | 2020-04-07 | 2020-04-07 | 20200405 |
| s.zh | engineer | 2 | zyq | 26000.00 | 2020-04-03 | 2020-04-03 | 20200405 |
| s.zh | tester | 3 | gkm | 20000.00 | 2020-04-07 | 2020-04-07 | 20200405 |
| x.qx | finance | 4 | pip | 13400.00 | 2020-04-07 | 2020-04-07 | 20200405 |
| x.qx | finance | 5 | kip | 24500.00 | 2020-04-07 | 2020-04-07 | 20200405 |
| x.qx | finance | 6 | zxxc | 13000.00 | 2020-04-07 | 2020-04-07 | 20200405 |
| x.qx | kiccp | 7 | xsz | 8600.00 | 2020-04-07 | 2020-04-07 | 20200405 |
| s.zh | engineer | 1 | szh | 28000.00 | 2020-04-07 | 2020-04-07 | 20200406 |
| s.zh | engineer | 2 | zyq | 26000.00 | 2020-04-03 | 2020-04-03 | 20200406 |
| s.zh | tester | 3 | gkm | 20000.00 | 2020-04-07 | 2020-04-07 | 20200406 |
| x.qx | finance | 4 | pip | 13400.00 | 2020-04-07 | 2020-04-07 | 20200406 |
| x.qx | finance | 5 | kip | 24500.00 | 2020-04-07 | 2020-04-07 | 20200406 |
| x.qx | finance | 6 | zxxc | 13000.00 | 2020-04-07 | 2020-04-07 | 20200406 |
| x.qx | kiccp | 7 | xsz | 8600.00 | 2020-04-07 | 2020-04-07 | 20200406 |
| s.zh | enginer | 1 | szh | 28000.00 | 2020-04-07 | 2020-04-07 | 20200407 |
| s.zh | enginer | 2 | zyq | 26000.00 | 2020-04-03 | 2020-04-03 | 20200407 |
| s.zh | tester | 3 | gkm | 20000.00 | 2020-04-07 | 2020-04-07 | 20200407 |
| x.qx | finance | 4 | pip | 13400.00 | 2020-04-07 | 2020-04-07 | 20200407 |
| x.qx | finance | 5 | kip | 24500.00 | 2020-04-07 | 2020-04-07 | 20200407 |
| x.qx | finance | 6 | zxxc | 13000.00 | 2020-04-07 | 2020-04-07 | 20200407 |
| x.qx | kiccp | 7 | xsz | 8600.00 | 2020-04-07 | 2020-04-07 | 20200407 |
+-----------------------------------+-------------------------------+------------------------------+--------------------------------+-----------------------------+----------------------------------+----------------------------------+-------------------------+
21 rows selected (0.153 seconds)
datacube_salary_dep_aggr 表的数据
+----------------------------------------+------------------------------------+----------------------------------------+
| datacube_salary_dep_aggr.company_name | datacube_salary_dep_aggr.dep_name | datacube_salary_dep_aggr.total_salary |
+----------------------------------------+------------------------------------+----------------------------------------+
| s.zh | enginer | 54000.00 |
| s.zh | tester | 20000.00 |
| x.qx | finance | 50900.00 |
| x.qx | kiccp | 8600.00 |
+----------------------------------------+------------------------------------+----------------------------------------+
4 rows selected (0.262 seconds)
场景六.通过让MAP 端, 多去承担任务, 去减少 Reducer 的计算成本 和 数据传输成本。
1)MAP JOIN 的方式
当两张数据表进行JOIN的时候,如果一张表的数据比较小. 比如维度表 这种,我们完全可以使用 MAP JOIN 的方式。
这个时候,我们需要把 小表 写在 JOIN 条件的左边。
Map JOIN 的使用场景 :
- 关联操作中有一张表非常小
- 不等值的链接操作
SQL demo
select /*+ mapjoin(c)*/ -- hint
c.tag
,b.yemaozi_pre
from
(
select
row_number() over(partition by 1 order by yemaozi_pre) yemaozi_rank
,yemaozi_pre
from customer_purchase_time_pre
) b -- 大表
join
(select
count(buyer_nick) total_buyer
,'yemaozi' as tag
from customer_purchase_time_pre
) c -- 小表,只有一行
where b.yemaozi_rank = round(c.total_buyer*0.9);
/*+mapjoin( c ) */ 这个语法里面的c 是join的小表的名字!
================================================
原理
MAP JOIN会把小表全部读入内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配,由于在map是进行了join操作,省去了reduce,运行的效率也会高很多。
使用mapjoin时,会先执行一个本地任务(mapreduce local task)将小表转成hashtable并序列化为文件再压缩,随后这些 hashtable文件会被上传到hadoop缓存,提供给各个mapjoin 使用。
================================================
分析
- 例子中的小表参与join,没使用map join时,会导致数据倾斜严重。某个reduce上落太多数据,reduce时内存会严重占用,甚至不足。
- 不等连接时,reduce会进行笛卡儿积,效率很低。
- 使用map join后 join操作会在map阶段完成。
=================================================
相关参数设置 :
hive.auto.convert.join : 是否自动转换为mapjoin
hive.mapjoin.smalltable.filesize : 小表的最大文件大小,默认为25000000,即25M
hive.auto.convert.join.noconditionaltask : 是否将多个mapjoin合并为一个
hive.auto.convert.join.noconditionaltask.size : 多个mapjoin转换为1个时,所有小表的文件大小总和的最大值。
注意 :不过,这个值只是限制输入的表文件的大小,并不代表实际mapjoin时,hashtable 的大小。
还有一些关于内存的参数设置
hive.mapjoin.localtask.max.memory.usage : 将小表转成hashtable的本地任务的最大内存使用率,默认0.9
hive.mapjoin.followby.gby.localtask.max.memory.usage : 如果mapjoin后面紧跟着一个group by任务,这种情况下 本地任务的最大内存使用率,默认是0.55
hive.mapjoin.check.memory.rows : localtask每处理完多少行,就执行内存检查。默认为100000
如果我们的localtask的内存使用超过阀值,任务会直接失败。
示例 :
--是否自动转换为mapjoin
set hive.auto.convert.join = true;
--小表的最大文件大小,默认为25000000,即25M
set hive.mapjoin.smalltable.filesize = 25000000;
--是否将多个mapjoin合并为一个
set hive.auto.convert.join.noconditionaltask = true;
--多个mapjoin转换为1个时,所有小表的文件大小总和的最大值。
set hive.auto.convert.join.noconditionaltask.size = 10000000;
其他的注意事项:
此外,使用mapjoin时还要注意,用作join的关联字段的字段类型最好要一致。
我就碰到一个诡异的问题,执行mapjoin 的local task时一直卡住,40万行的小表处理了好几个小时,正常情况下应该几秒钟就完成了。查了好久原因,结果原来是做join的关联字段的类型不一致,一边是int, 一边是string,hive解释计划里显示它们都会被转成double再来join。我把字段类型改为一致的,瞬间就快了。照理说就算转成double也不该这么慢,不知道是不是hive的bug。
下面,我们根据最开始构造的测试数据,演示一下 MAP JOIN
EXPLAIN
SELECT
a.*
FROM datacube_salary_org AS a
JOIN datacube_salary_dep_aggr AS b
ON a.company_name = b.company_name
;
由于我们没有用 hint 标志,我们需要显示开启下 自动转化为 map join ( 这个参数是默认开启的,这里只是手动设置下 )
set hive.auto.convert.join = true;
INFO : Starting task [Stage-5:EXPLAIN] in serial mode
INFO : Completed executing command(queryId=hive_20200415024514_56f33fa3-a2e7-45e7-b649-f92ed288eb2d); Time taken: 0.023 seconds
INFO : OK
+----------------------------------------------------+
| Explain |
+----------------------------------------------------+
| STAGE DEPENDENCIES: |
| Stage-4 is a root stage |
| Stage-3 depends on stages: Stage-4 |
| Stage-0 depends on stages: Stage-3 |
| |
| STAGE PLANS: |
| Stage: Stage-4 |
| Map Reduce Local Work |
| Alias -> Map Local Tables: |
| b |
| Fetch Operator |
| limit: -1 |
| Alias -> Map Local Operator Tree: |
| b |
| TableScan |
| alias: b |
| filterExpr: company_name is not null (type: boolean) |
| Statistics: Num rows: 4 Data size: 1160 Basic stats: COMPLETE Column stats: NONE |
| Filter Operator |
| predicate: company_name is not null (type: boolean) |
| Statistics: Num rows: 4 Data size: 1160 Basic stats: COMPLETE Column stats: NONE |
| HashTable Sink Operator |
| keys: |
| 0 company_name (type: string) |
| 1 company_name (type: string) |
| |
| Stage: Stage-3 |
| Map Reduce |
| Map Operator Tree: |
| TableScan |
| alias: a |
| filterExpr: company_name is not null (type: boolean) |
| Statistics: Num rows: 7 Data size: 1628 Basic stats: COMPLETE Column stats: PARTIAL |
| Filter Operator |
| predicate: company_name is not null (type: boolean) |
| Statistics: Num rows: 7 Data size: 1288 Basic stats: COMPLETE Column stats: PARTIAL |
| Map Join Operator |
| condition map: |
| Inner Join 0 to 1 |
| keys: |
| 0 company_name (type: string) |
| 1 company_name (type: string) |
| outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, _col6, _col7 |
| Statistics: Num rows: 7 Data size: 1416 Basic stats: COMPLETE Column stats: NONE |
| File Output Operator |
| compressed: false |
| Statistics: Num rows: 7 Data size: 1416 Basic stats: COMPLETE Column stats: NONE |
| table: |
| input format: org.apache.hadoop.mapred.SequenceFileInputFormat |
| output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat |
| serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe |
| Local Work: |
| Map Reduce Local Work |
| |
| Stage: Stage-0 |
| Fetch Operator |
| limit: -1 |
| Processor Tree: |
| ListSink |
| |
+----------------------------------------------------+
60 rows selected (0.209 seconds)
2) MAP AGGR , 在 Map 端进行预聚合
我们在做 GROUP BY , DISTINCT 这些都是可以在 Map 端进行预聚合的,
Map端聚合设置的参数
set hive.map.aggr=true;