• 百亿级数据处理优化


    最近在做大数据处理时,遇到两个大表 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 的数据量,同时优化没有放之四海而皆准的方法,一定是结合业务进行的。

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

  • 相关阅读:
    python 基于os模块的常用操作
    python 文件的读写
    Spring Boot 2.0(五):Docker Compose + Spring Boot + Nginx + Mysql 实践
    Docker(四):Docker 三剑客之 Docker Compose
    Docker(三):Dockerfile 命令详解
    Docker(二):Dockerfile 使用介绍
    Docker(一):Docker入门教程
    虚拟机vmware centos7 扩展磁盘空间
    那些年我们遇到的坑(1)-Description Resource Path Location Type Archive for required library
    RPM安装命令总结
  • 原文地址:https://www.cnblogs.com/duma/p/11186279.html
Copyright © 2020-2023  润新知