zoukankan      html  css  js  c++  java
  • 百亿级数据处理优化

    最近在做大数据处理时,遇到两个大表 join 导致数据处理太慢(甚至算不出来)的问题。我们的数仓基于阿里的 ODPS,它与 Hive 类似,所以这篇文章也适用于使用 Hive 优化。处理优化问题,一般是先指定一些常用的优化参数,但是当设置参数仍然不奏效的时候,我们就要结合具体的业务,在 SQL 上做优化了。为了不增加大家的阅读负担,我会简化这篇文章的业务描述

    问题

    这是一个离线数据处理的问题。在这个业务中有两张表,表结构及说明如下:

    user_article_tb 表:

    字段解释:

     uid: 用户标识,itemid:文章id,dur: 阅读文章时长,如果大于 0 代表阅读了文章,等于 0 代表没有点击文章 

     dt:天分区,每天 55 亿条记录

    user_profile_tb 表:
    字段解释:
    uid:用户标识,gender:性别,F 代表女,M 代表男,age:年龄,city:城市
    dt:天分区字段,这是一张总表,每天存储全量用户画像属性,最新数据十亿级别

    需求是这样的:计算 7 天中,女性用户在每篇文章上的 ctr (最终会按照降序进行截断)。直接写 SQL 很容易,如下:

    select 
      itemid
      , count(if(dur > 0, 1, null)) / count(1) ctr
    from
      (
          select uid, itemid, dur
          from user_article_tb
          where dt>='20190701' and dt<='20190707'
      ) data_tb
      join
      (
        select *
        from user_profile_tb
        where dt='20190707' --最新的日期
           and gender='F'
      ) profile_tb
      on 
        data_tb.uid = profile_tb.uid
    group by 
      itemid
    order by ctr desc
    limit 50000
    ;

    那么问题来了:

    • 对于 user_article_tb 来说,7天的数据量将近 400 亿条记录,还需要 join 一张十亿级别的画像表。这个数据量基本上就跑不出来了
    • 像这种探索性质的需求,经常会变化。假设需求变成计算男性或者计算一二线城市用户的呢?可能又需要重跑整个数据,既要付出时间成本又要付出高昂的资源成本

    解决

    我们一一解决上面提到的两个问题。先考虑第一个,既然 join 的两张表太大了,我们能不能尝试把表变小呢。答案是肯定的,对于画像表来说显然是没办法缩小了,但是对于 user_artitle_tb 是可以的。我们可以按照表的分区字段 dt 用每天的数据分别 join 画像表,将结果再按天存储在一张临时表里面。这样每天就是十亿级别的数据 join,基本可以解决问题。但是每天的数据仍有多余的 join,比如:某天的数据中 uid = 00001 的用户,一天看了 1000 篇文章,那这个用户就需要多 join 999 次。在我们的业务中一个用户一天看文章的数量 > 10 是很普遍的,因此多余 join 的情况还是比较严重的。

    针对上面提到的多余 join 的情况,最彻底的解决方法就是把 user_article_tb 表变成 uid 粒度的,跟画像表一样。我们将 7 天的数据转换成 uid 粒度的 SQL 如下:

    insert overwrite table user_article_uid_tb as 
    select uid, wm_concat(':', concat_ws(',', itemid, dur)) item_infos
    from 
      (
         select *
        from user_article_tb
         where dt >= '20190701' and dt <= '20190707'   
      ) tmp
    group by uid

    从上面 SQL 可以看到,我们首先将 7 天的数据按照 uid 做 group by 操作,构造 item_infos。因为我们的是计算 ctr,所以我们可以按照 uid 粒度对表做转换,并且 item_infos 字段包含什么是要根据业务需求做选择。每天不到 1 亿 uid,7天汇总的 uid 不到 10 亿,两张 uid 粒度的表进行 join 就会快很多。

    至此,多余 join 的问题得到了解决, 再来看看第二个问题。这个问题其实就是我们维度建模理论中所说的宽表,为了避免统计不同维度时频繁 join 维表,我们可以在上游数据将常用的维度提前关联起来,形成一张大宽表。下游数据可以直接用从而减少 join。以我们的问题为例,SQL 如下:

    create table user_profile_article_uid_tb as
    select 
        data_tb.uid
        , item_infos
        , gender
        , age
        , city
      -- 其他维度字段
    from
      (
          select uid, item_infos
          from user_article_uid_tb 
      ) data_tb
      join
      (
          select uid, gender, age, city
        from user_profile_tb
        where dt='20190707' --最新的日期
      ) profile_tb
      on 
        data_tb.uid = profile_tb.uid
    ;

    这样,上面提到的两个问题就都解决了。最终我们的需求:女性用户每篇文章的 ctr 计算如下:

    select 
        itemid
        , count(if(dur > 0, 1, null)) / count(1) ctr
    from 
      (
        select 
          split(item_info, ',')[0] itemid     , split(item_info, ',')[1] dur     from user_profile_article_uid_tb     lateral view explode(split(item_infos, ':')) item_tb as item_info   ) tmp group itemid order by ctr desc limit 50000

    参数优化

    mapreduce.map.memory.mb
    mapreduce.reduce.memory.mb
    mapred.reduce.tasks

    这些参数设置是比较通用的选项, 当这些选项不能够达到最优的效果时,需要从业务上进行优化。

    小结

    这篇文章主要介绍了在 ODPS 或 Hive 上,百亿级数据规模的 join 优化。核心思想就是减少 join 的数据量,同时优化没有放之四海而皆准的方法,一定是结合业务进行的。

    欢迎关注公众号「渡码」,一起见证成长

  • 相关阅读:
    Samba 4.0 RC3 发布
    SymmetricDS 3.1.7 发布,数据同步和复制
    Express.js 3.0 发布,Node.js 的高性能封装
    GIFLIB 5.0.1 发布,C语言的GIF处理库
    jQuery UI 1.9.1 发布
    SVN Access Manager 0.5.5.14 发布 SVN 管理工具
    DynamicReports 3.0.3 发布 Java 报表工具
    HttpComponents HttpClient 4.2.2 GA 发布
    AppCan 2.0 正式发布,推移动应用云服务
    Ruby 2.0 的新功能已经冻结
  • 原文地址:https://www.cnblogs.com/duma/p/11186279.html
Copyright © 2011-2022 走看看