百億級數據處理優化

最近在作大數據處理時,遇到兩個大表 join 致使數據處理太慢(甚至算不出來)的問題。咱們的數倉基於阿里的 ODPS,它與 Hive 相似,因此這篇文章也適用於使用 Hive 優化。處理優化問題,通常是先指定一些經常使用的優化參數,可是當設置參數仍然不奏效的時候,咱們就要結合具體的業務,在 SQL 上作優化了。爲了避免增長你們的閱讀負擔,我會簡化這篇文章的業務描述。sql

問題

這是一個離線數據處理的問題。在這個業務中有兩張表,表結構及說明以下:bash

user_article_tb 表:複製代碼

字段解釋:
uid: 用戶標識,itemid:文章id,dur: 閱讀文章時長,若是大於 0 表明閱讀了文章,等於 0 表明沒有點擊文章
dt:天分區,天天 55 億條記錄複製代碼

user_profile_tb 表:複製代碼
字段解釋: 
uid:用戶標識,gender:性別,F 表明女,M 表明男,age:年齡,city:城市 
dt:天分區字段,這是一張總表,天天存儲全量用戶畫像屬性,最新數據十億級別複製代碼

需求是這樣的:計算 7 天中,女性用戶在每篇文章上的 ctr (最終會按照降序進行截斷)。直接寫 SQL 很容易,以下:markdown

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 以下:ui

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 就會快不少。spa

至此,多餘 join 的問題獲得瞭解決, 再來看看第二個問題。這個問題其實就是咱們維度建模理論中所說的寬表,爲了不統計不一樣維度時頻繁 join 維表,咱們能夠在上游數據將經常使用的維度提早關聯起來,造成一張大寬表。下游數據能夠直接用從而減小 join。以咱們的問題爲例,SQL 以下:code

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 計算以下:orm

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複製代碼

這些參數設置是比較通用的選項, 當這些選項不可以達到最優的效果時,須要從業務上進行優化。ci

小結

這篇文章主要介紹了在 ODPS 或 Hive 上,百億級數據規模的 join 優化。核心思想就是減小 join 的數據量,同時優化沒有放之四海而皆準的方法,必定是結合業務進行的。

歡迎關注公衆號「渡碼」,一塊兒見證成長

相關文章
相關標籤/搜索