1. 程式人生 > >hive優化-count(distinct)

hive優化-count(distinct)






問題描述

COUNT(DISTINCT xxx)在hive中很容易造成資料傾斜。針對這一情況,網上已有很多優化方法,這裡不再贅述。
但有時,“資料傾斜”又幾乎是必然的。我們來舉個例子:

假設表detail_sdk_session中記錄了訪問某網站M的客戶端會話資訊,即:如果使用者A開啟app客戶端,則會產生一條會話資訊記錄在該表中,該表的粒度為“一次”會話,其中每次會話都記錄了使用者的唯一標示uuid,uuid是一個很長的字串,假定其長度為64位。現在的需求是:每天統計當月的活用使用者數——“月活躍使用者數”(當月訪問過app就為活躍使用者)。我們以2016年1月為例進行說明,now表示當前日期。
最簡單的方法

這個問題邏輯上很簡單,SQL也很容易寫出來,例如:

SELECT
  COUNT(DISTINCT uuid)
FROM detail_sdk_session t
WHERE t.date >= '2016-01-01' AND t.date <= now
  
  • 1
  • 2
  • 3
  • 4

上述SQL程式碼中,now表示當天的日期。很容易想到,越接近月末,上面的統計的資料量就會越大。更重要的是,在這種情況下,“資料傾斜”是必然的,因為只有一個reducer在進行COUNT(DISTINCT uuid)的計算,所有的資料都流向唯一的一個reducer,不傾斜才怪。

優化1

其實,在COUNT(DISTINCT xxx)的時候,我們可以採用“分治”的思想來解決。對於上面的例子,首先我們按照uuid的前n位進行GROUP BY,並做COUNT(DISTINCT )操作,然後再對所有的COUNT(DISTINCT)結果進行求和。
我們先把SQL寫出來,然後再做分析。

-- 外層SELECT求和
SELECT
  SUM(mau_part) mau
FROM
(
  -- 內層SELECT分別進行COUNT(DISTINCT)計算
  SELECT
    substr(uuid, 1, 3) uuid_part,
    COUNT(DISTINCT
substr(uuid, 4)) AS mau_part FROM detail_sdk_session WHERE partition_date >= '2016-01-01' AND partition_date <= now GROUP BY substr(uuid, 1, 3) ) t;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

上述SQL中,內層SELECT根據uuid的前3位進行GROUP BY,並計算相應的活躍使用者數COUNT(DISTINCT),外層SELECT求和,得到最終的月活躍使用者數。
這種方法的好處在於,在不同的reducer各自進行COUNT(DISTINCT)計算,充分發揮hadoop的優勢,然後進行求和。

注意,上面SQL中,n設為3,不應過大。
為什麼n不應該太大呢?我們假定uuid是由字母和數字組成的:大寫字母、小寫字母和數字,字元總數為26+26+10=62。理論上,內層SELECT進行GROUP BY時,會有 62^n 個分組,外層SELECT就會進行 62^n 次求和。所以n不宜過大。當然,如果資料量十分巨大,n必須充分大,才能保證內層SELECT中的COUNT(DISTINCT)能夠計算出來,此時可以再巢狀一層SELECT,這裡不再贅述。

優化2

其實,很多部落格中都記錄了使用GROUP BY 操作代替 COUNT(DISTINCT) 操作,但有時僅僅使用GROUP BY操作還不夠,還需要加點小技巧。
還是先來看一下程式碼:

--  第三層SELECT
SELECT
  SUM(s.mau_part) mau
FROM
(
  -- 第二層SELECT
  SELECT
    tag,
    COUNT(*) mau_part
  FROM
  (
      -- 第一層SELECT
    SELECT
      uuid, 
      CAST(RAND() * 100 AS BIGINT) tag  -- 為去重後的uuid打上標記,標記為:0-100之間的整數。
    FROM detail_sdk_session
    WHERE partition_date >= '2016-01-01' AND partition_date <= now
    GROUP BY uuid   -- 通過GROUP BY,保證去重
   ) t
  GROUP BY tag
) s
;
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22


  1. 第一層SELECT:對uuid進行去重,併為去重後的uuid打上整數標記
  2. 第二層SELECT:按照標記進行分組,統計每個分組下uuid的個數
  3. 第三層SELECT:對所有分組進行求和
    上面這個方法最關鍵的是為每個uuid進行標記,這樣就可以對其進行分組,分別計數,最後去和。如果資料量確實很大,也可以增加分組的個數。例如:CAST(RAND() * 1000 AS BIGINT) tag
    —-EOF—–
    Categories: hive Tags: hive COUNT DISTINCT



原文地址: Hive之COUNT DISTINCT優化





問題描述