SQL篇—Hive数据倾斜(二)

这是面试经常遇到的问题,虽然我还没有机会在实战中面对过。

  • 1.参考文章:
    Hive的数据倾斜:
    https://www.cnblogs.com/qingyunzong/p/8847597.html

    https://www.cnblogs.com/kongcong/p/7777092.html
    https://www.jianshu.com/p/ab08de658adc

  • 2.表现:进度卡在99%,某一个reduce子任务总比其他时间长

  • 3.易出现数据倾斜的情况
    a. join: 其中一个表小,但key集中/分桶的判断字段0值或空值较多
    b.group by: 维度过小,某值的数量过多
    c. count distinct: 某特殊值过多

  • 4.原因:
    key分布不均
    建表时考虑不周
    业务数据本身特性
    某些SQL本身

  • 5.solution:
    a.表小但key集中
    产生新的key,对应的数据分布尽可能均匀

    map join:将其中做连接的小表(全量数据)分发到所有 MapTask 端进行 Join,从而避免reduceTask,前提要求是内存足以装下该全量数据
    用法:在select后面添加/* +mapjoin(tablelist)*/提示优化器转化为map join ,通常tablelist是小表,全量发送到内存

    select /* +mapjoin(a) */ a.id aid, name, age from a join b on a.id = b.id;
    

    Hive0.11版本以后会自动开启mapjoin优化,由两个参数设置:
    set hive.auto.convert.join=true; //设置 MapJoin 优化自动开启
    set hive.mapjoin.smalltable.filesize=25000000 //设置小表不超过多大时开启 mapjoin 优化

    但如果小表太大,无法全量发送,那么需要对上述方法进行改进。
    如日志表和用户表的连接:

    select * from log a left outer join users b on a.user_id = b.user_id;
    

    相对于log,user表是小表但量很大,改进:

    select /*+mapjoin(x)*/* from log a
    left outer join (
     select /*+mapjoin(c)*/ d.*
     from ( select distinct user_id from log ) c join users d on c.user_id = d.user_id
    ) x
    on a.user_id = x.user_id;
    

    b.空值较多
    将空值的key变成字符串+随机数

    select * from log a 
    left outer join user b on
    case when a.user_id is null then concat('hive',rand()) else a.user_id end = b.user_id;
    

    c.不同数据类型关联
    user表中 user_id 字段为 int,log 表中 user_id 为既有 string 也有 int 的类型, 进行 join 操作的时候,默认的 hash 操作会按照 int 类型的 id 进 行分配,这样就会导致所有的 string 类型的 id 就被分到同一个 reducer 当中

    select * from user a left outer join log b on b.user_id = cast(a.user_id as string)
    

推荐阅读更多精彩内容