0%

Hive MapJoin优化实践

MapJoin Optimization

主要的几个优化参数:

  1. smalltable的大小,超过这个值才会使用map join
  2. map join的合并
  3. local task内存: mapjoin的小表转换为hashtable后的大小阈值,超过了会报错
  4. 本地任务可以使用的最大内存比例
  5. join条件字段类型需要一致
  6. map节点的内存大小

在Hive中,common join是很慢的,如果我们是一张大表关联多张小表,可以使用mapjoin加快速度。

mapjoin主要有以下参数:

1
2
3
4
5
6
7
8
--是否自动转换为mapjoin
hive.auto.convert.join
--小表的最大文件大小,默认为25000000,即25M
hive.mapjoin.smalltable.filesize
--是否将多个mapjoin合并为一个
hive.auto.convert.join.noconditionaltask
--多个mapjoin转换为1个时,所有小表的文件大小总和的最大值
hive.auto.convert.join.noconditionaltask.size

例如,一个大表顺序关联3个小表a(10M), b(8M),c(12M),
如果hive.auto.convert.join.noconditionaltask.size的值:

  1. <=18M,则无法合并mapjoin,必须执行3个mapjoin;
  2. 18M <30M,则可以合并a和b表的mapjoin,所以只需要执行2个mapjoin;

  3. 30M,则可以将3个mapjoin都合并为1个。

合并mapjoin

合并mapjoin有啥好处呢?
因为每个mapjoin都要执行一次map,需要读写一次数据,所以多个mapjoin就要做多次的数据读写,合并mapjoin后只用读写一次,自然能大大加快速度。
但是执行map是内存大小是有限制的,在一次map里对多个小表做mapjoin就必须把多个小表都加入内存,为了防止内存溢出,所以加了hive.auto.convert.join.noconditionaltask.size参数来做限制。不过,这个值只是限制输入的表文件的大小,并不代表实际mapjoinhashtable的大小。

我们可以通过explain查看执行计划,来看看mapjoin是否生效。

具体示例

1
2
3
4
set hive.auto.convert.join=true;
set hive.mapjoin.smalltable.filesize=300000000;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=300000000;

使用mapjoin时,会先执行一个本地任务(mapreduce local task)将小表转成hashtable并序列化为文件再压缩,随后这些hashtable文件会被上传到hadoop缓存,提供给各个mapjoin使用。这里有三个参数我们需要注意:

local task memory: 小表转换成hashtable的内存阈值

1
2
3
4
5
6
--将小表转成hashtable的本地任务的最大内存使用率,默认0.9
hive.mapjoin.localtask.max.memory.usage
--如果mapjoin后面紧跟着一个group by任务,这种情况下 本地任务的最大内存使用率,默认是0.55
hive.mapjoin.followby.gby.localtask.max.memory.usage
--localtask每处理完多少行,就执行内存检查。默认为100000
hive.mapjoin.check.memory.rows

如果我们的localtask的内存使用超过阀值,任务会直接失败。

字段类型要一致

此外,使用mapjoin时还要注意,用作join的关联字段的字段类型最好要一致。

我就碰到一个诡异的问题,执行mapjoin 的local task时一直卡住,40万行的小表处理了好几个小时,正常情况下应该几秒钟就完成了。查了好久原因,结果原来是做join的关联字段的类型不一致,一边是int, 一边是string,hive解释计划里显示它们都会被转成double再来join。我把字段类型改为一致的,瞬间就快了。照理说就算转成double也不该这么慢,不知道是不是hive的bug。

1
2
3
4
5
6
7
8
--是否自动转换为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;

hive的join 有一种优化的方式:map join

但是,使用这种优化的时候要小心一点,先说一下优化配置的参数:

localtask max memory usage: 本地任务内存百分比

1
2
3
4
set hive.optimize.correlation=true
set hive.auto.convert.join=true
set hive.mapjoin.localtask.max.memory.usage=0.99
hive.mapjoin.localtask.max.memory.usage

说明:本地任务可以使用内存的百分比 默认值: 0.90,如果你的localtask mapjoin 表很小可以试试,但彻底解决需要

set hive.auto.convert.join=false;关闭自动mapjoin 但这个参数用的时候一定要注意,

如果你的sql 很长join会常多,关闭mapjoin任务数会成10倍激增,contener满了任务同样会非常之慢,
set hive.auto.convert.join=false;一定要用在localtask级别这种超轻量及的job上。

local mem: map节点的内存大小

1
2
--设置本地memory的大小值,单位为M
hive.mapred.local.mem