• Hive数据倾斜


    一、数据倾斜表现:

      在MapReduce编程模型中十分常见,大量相同的key被分配到一个reduce里,造成一个reduce任务累死,其他reduce任务闲死。查看任务进度,发现长时间停留在99%或100%,查看任务监控界面,只有少量的reduce子任务未完成。

    1、join数据倾斜表现、定位过程

    1.查了5个小时还没结束的sql语句

    set mapred.reduce.tasks = 30;insert overwrite directory 'xxx' select cus.idA,cus.name,addr.bb from tableA as cus join tableB as addr on cus.idA = addr.idB

      很简单的一个hql语句,优化的空间也不是很大(例子中的addr数据量比cus小,应该讲addr放在前面驱动join)。tableA的量级为亿级,tableB的量级为几百万级别。就这么一个简单的sql,尼玛从上午十点半开始跑,跑到下午三点半还没有跑完。实在受不了了,kill掉了。

    2.初步分析

    首先上个查询过程中的图

      看到这种情况,稍微有点经验的同学第一反应肯定就是:卧槽,这尼玛肯定是数据倾斜了。没错,map早就完工了,reduce阶段一直卡在99%,而且cumulative cpu的时间还一直在增长,说明整个job还在后台跑着。这种情况下,99%的可能性就是数据发生了倾斜,整个查询任务都在等某个节点完成。。。

    3.分析那部分数据产生了倾斜

      问题既然已经定位了,那接下来就是需要解决问题了。正好不巧的是,集群这几天还出了一些状况。so,首先为了确认到底是集群本身的问题,还是代码的问题,先找了另外两个表,都是亿级数据。这两个表不存在数据倾斜的情况,join一把试了试,两分钟之内结果就出来了。万幸,说明这会集群已经没有问题了,还是查查数据跟代码吧。

      代码本身很简单,那就沿着数据倾斜的方向查查吧。因为上面的两个表是根据id关联的,那如果倾斜的话,肯定就是id倾斜了哇。

    set mapred.reduce.tasks = 5;
    select idA,count(*) as num from tableA group by idA distribute by idA sort by num desc limit 10

    结果为:

    192928  5828529
    2000000000496592833 2406289
    18000   1706031
    4000288 1386324
    2000000003624295444 1201178
    2000000001720892923 1029475
    2000000002292880478 991299
    2000000000736661289 881954
    2000000000740899183 873487
    2000000000575115116 803250

      对于有上亿数据的一个表来说,这数据也算不上倾斜多厉害嘛。最多的一个key也就五百多万不到六百万。好吧,先不管了,再查一把另外一个表

    set mapred.reduce.tasks = 5;select idB,count(*) as num from tableB group by idB distribute by idB sort by num desc limit 10

    结果

    192928  383412
    18000   60318
    617279581   23028
    51010262    4643
    4000286 3528
    2000000000575115116 3218
    1366173280  3012
    4212339 2972
    2000000002025620390 2704
    2000000001312577574 2622

      这数据倾斜,也不是特别严重嘛。

      不过再把这两个结果一对比。两个表里最多的一个key都是192928,一个出现了将近600万次,一个出现了将近40万次。这两个表再一join,尼玛这一个key就是600万*40万的计算量。最要命的是,这计算量都分配给了一个节点。我数学不太好,600万*40万是多少,跪求数学好的同学帮忙计算一下。不过根据经验来看的话,别说5个小时,再添个0也未必能算得完。。。

    4.如何解决

      既然找到了数据倾斜的位置,那解决起来也就好办了。因为本博主的真正需求并不是真正要算两个表的笛卡尔积(估计实际中也极少有真正的需求算600万*40万数据的笛卡尔积。如果有,那画面太美我不敢看),所以最easy的解决方案,就是将这些key给过滤掉完事:

    set mapred.reduce.tasks = 30;
    insert overwrite directory 'xxx' 
    select cus.idA,cus.name,addr.bb from tableA as cus 
    join tableB as addr on cus.idA = addr.idB 
    where cus.idA not in 
    (192928,2000000000496592833,18000,4000288,2000000003624295444,2000000001720892923,2000000002292880478,2000000000736661289,2000000000740899183,2000000000575115116,617279581,51010262,4000286,1366173280,2000000002025620390,2000000001312577574)sks = 30;insert overwrite directory 'xxx' select cus.idA,cus.name,addr.bb from tableA as cus join tableB as addr on cus.idA = addr.idB where cus.idA not in (192928,2000000000496592833,18000,4000288,2000000003624295444,2000000001720892923,2000000002292880478,2000000000736661289,2000000000740899183,2000000000575115116,617279581,51010262,4000286,1366173280,2000000002025620390,200000000131257757

    将此代码重新提交,5min时间,job跑完收工!

    二、常发生数据倾斜的业务场景:

    1、join数据倾斜

    (1)小表join大表,小表key比较集中,有几个key比较多:

    解决方式:使用map join,让小表先进入内存,在map端就在每个分区进行一次join,减少网络传输,在ruduce阶段在进行一次最终的聚合。

    原理:

    SELECT
            pis.id_ id,
            service_name serviceName,
            service_code serviceCode,
            bd.code_text serviceType,
        FROM
            prd_price_increment_service pis
            left join prd_price_increment_product pip on pis.id_ = pip.increment_service_id

      a、common join的弊端:

      common join的原理是利用map将数据从数据源加载过来,并将on后面的条件当做key,将要查询的字段+tag当做value,其中tag是用来标识value是来自哪一张表。然后通过shuffle,利用key的hash值,将其传输到不同的reduce中。然后在reduce当中按照key进行聚合操作。返回查询结果。

            这其中的弊端就是map端没有预聚合,所有的key,value都要经过网络传输,而所有的聚合操作都在reduce当中,容易发生数据倾斜。

      b、map join的好处:

            map join是在小表join大表的场景。先将小表加载到所有节点的内存当中,直接在map阶段,在每个分区中就先进行一次join操作,相当于进行了预聚合。然后再讲聚合后的结果,shuffle到reduce当中,再进行最终的聚合操作。

           这样做的好处就是使用map端预先join进行预聚合,减少网络传输,减轻reduce压力,解决数据倾斜问题。

    mapjoin在sql中使用:

    
    
    SELECT
        /*+ mapjoin(pis)*/
            pis.id_ id,
            service_name serviceName,
            service_code serviceCode,
            bd.code_text serviceType,
        FROM
            prd_price_increment_service pis
            left join prd_price_increment_product pip on pis.id_ = pip.increment_service_id
    
    

      在进行sql执行过程中,由于有时候系统自动优化的方式并不是最优的。需要我们手工添加hint来提高查询效率。

    hint用法:https://blog.csdn.net/Evils798/article/details/8648156

    ruduce join 和 map join的原理:https://www.cnblogs.com/qiuhong10/p/7698277.html

    (2)大表join大表,但表中作为关联条件的字段0值或空值比较多。

    解决方式:给空值添加随机key值,将其分发到不同的reduce中处理。因为空值不参与join,所以对结果无影响。

    select *
    from log a left join users b 
    on case when a.user_id is Null then concat('dp_hive',rand()) else a.user_id end = b.user_id;

    2、group by数据倾斜

    (1)group by聚合倾斜时某个类型得数量过多:

    产生原因:分组的维度过少,每个维度的值过多,并且某种类型的数据过多,其他类型的数据过少,导致处理某类数据的时候reduce耗时大。因此,当按照类型进行分组的时候,会将相同的类型分到同一个节点的reduce当中,导致某个节点的reduce处理速度过慢。一直看到map100% reduce99%的情况。

    解决方法:开启map端预聚合、数据倾斜数据自动优化

    hive.map.aggr=true;

    hive.groupby.skewindata=true;

    原理:

    a、设置map端聚合。

    SELECT * FROM logs;
    a    苹果    5
    a    橙子    3
    a      苹果   2
    b    烧鸡    1
     
    hive> SELECT uid, SUM(COUNT) FROM logs GROUP BY uid;
    a    10
    
    b     1

    b、生成的查询计划会有两个MR job,第一个 mr 的 map 阶段后,将结果随机分配到reduce当中,每个reduce做局部聚合操作,并输出结果。第二个MR job 再将之前的预处理结果进行正常的group by,完成最终的聚合。

    3、count distinct某个特殊值过多。

    count distinct 某个特殊值过多

    https://www.jianshu.com/p/6356f18210cc 

    参考博客mapjoin:https://www.cnblogs.com/qiuhong10/p/7698277.html

    https://blog.csdn.net/qq_24505127/article/details/101687227

    https://blog.csdn.net/i000zheng/article/details/80733327

  • 相关阅读:
    MIX11大会WP7主题演讲中文字幕版
    日本战神——源义经
    System.Web.HttpUtility for .Net Compact Framework
    VS2010 SP1
    <如何成为一个成功的职业经理人>读书笔记2
    <左手曾国藩,右手胡雪岩>读书笔记
    <福布斯荐75本经商必读06基业长青>读书笔记
    <中国人聪明之道>读书笔记
    <浮沉>读书笔记
    <79个潜规则:改变生活的心理学法则>读书笔记
  • 原文地址:https://www.cnblogs.com/guoyu1/p/12002374.html
Copyright © 2020-2023  润新知