Hive之COUNT DISTINCT優(yōu)化

COUNT(DISTINCT xxx) 在hive中很容易造成數(shù)據(jù)傾斜。針對(duì)這一情況,網(wǎng)上已有很多優(yōu)化方法,這里不再贅述。

但有時(shí),“數(shù)據(jù)傾斜”又幾乎是必然的。我們來(lái)舉個(gè)例子:

假設(shè)表detail_sdk_session中記錄了訪問(wèn)某網(wǎng)站M的客戶端會(huì)話信息,即:如果用戶A打開(kāi)app客戶端,則會(huì)產(chǎn)生一條會(huì)話信息記錄在該表中,該表的粒度為“一次”會(huì)話,其中每次會(huì)話都記錄了用戶的唯一標(biāo)示uuid,uuid是一個(gè)很長(zhǎng)的字符串,假定其長(zhǎng)度為64位。現(xiàn)在的需求是:每天統(tǒng)計(jì)當(dāng)月的活用用戶數(shù)——“月活躍用戶數(shù)”(當(dāng)月訪問(wèn)過(guò)app就為活躍用戶)。我們以2016年1月為例進(jìn)行說(shuō)明,now表示當(dāng)前日期。

最簡(jiǎn)單的方法

這個(gè)問(wèn)題邏輯上很簡(jiǎn)單,SQL也很容易寫出來(lái),例如:

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

上述SQL代碼中,now表示當(dāng)天的日期。很容易想到,越接近月末,上面的統(tǒng)計(jì)的數(shù)據(jù)量就會(huì)越大。更重要的是,在這種情況下,“數(shù)據(jù)傾斜”是必然的,因?yàn)橹挥幸粋€(gè)reducer在進(jìn)行COUNT(DISTINCT uuid)的計(jì)算,所有的數(shù)據(jù)都流向唯一的一個(gè)reducer,不傾斜才怪。

優(yōu)化1

其實(shí),在COUNT(DISTINCT xxx)的時(shí)候,我們可以采用“分治”的思想來(lái)解決。對(duì)于上面的例子,首先我們按照uuid的前n位進(jìn)行GROUP BY,并做COUNT(DISTINCT )操作,然后再對(duì)所有的COUNT(DISTINCT)結(jié)果進(jìn)行求和。

我們先把SQL寫出來(lái),然后再做分析。

-- 外層SELECT求和
SELECT
  SUM(mau_part) mau
FROM
(
  -- 內(nèi)層SELECT分別進(jìn)行COUNT(DISTINCT)計(jì)算
  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;

上述SQL中,內(nèi)層SELECT根據(jù)uuid的前3位進(jìn)行GROUP BY,并計(jì)算相應(yīng)的活躍用戶數(shù)COUNT(DISTINCT),外層SELECT求和,得到最終的月活躍用戶數(shù)。

這種方法的好處在于,在不同的reducer各自進(jìn)行COUNT(DISTINCT)計(jì)算,充分發(fā)揮hadoop的優(yōu)勢(shì),然后進(jìn)行求和。

注意,上面SQL中,n設(shè)為3,不應(yīng)過(guò)大。

為什么n不應(yīng)該太大呢?

我們假定uuid是由字母和數(shù)字組成的:大寫字母、小寫字母和數(shù)字,字符總數(shù)為26+26+10=62。

理論上,內(nèi)層SELECT進(jìn)行GROUP BY時(shí),會(huì)有 62^n 個(gè)分組,外層SELECT就會(huì)進(jìn)行 62^n 次求和。所以n不宜過(guò)大。

當(dāng)然,如果數(shù)據(jù)量十分巨大,n必須充分大,才能保證內(nèi)層SELECT中的COUNT(DISTINCT)能夠計(jì)算出來(lái),此時(shí)可以再嵌套一層SELECT,這里不再贅述。

優(yōu)化2

其實(shí),很多博客中都記錄了使用GROUP BY 操作代替 COUNT(DISTINCT) 操作,但有時(shí)僅僅使用GROUP BY操作還不夠,還需要加點(diǎn)小技巧。

還是先來(lái)看一下代碼:

--  第三層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打上標(biāo)記,標(biāo)記為:0-100之間的整數(shù)。
    FROM detail_sdk_session
    WHERE partition_date >= '2016-01-01' 
      AND partition_date <= now
      AND uuid IS NOT NULL
    GROUP BY uuid   -- 通過(guò)GROUP BY,保證去重
   ) t
  GROUP BY tag
) s
;
  1. 第一層SELECT:對(duì)uuid進(jìn)行去重,并為去重后的uuid打上整數(shù)標(biāo)記
  2. 第二層SELECT:按照標(biāo)記進(jìn)行分組,統(tǒng)計(jì)每個(gè)分組下uuid的個(gè)數(shù)
  3. 第三層SELECT:對(duì)所有分組進(jìn)行求和

上面這個(gè)方法最關(guān)鍵的是為每個(gè)uuid進(jìn)行標(biāo)記,這樣就可以對(duì)其進(jìn)行分組,分別計(jì)數(shù),最后去和。如果數(shù)據(jù)量確實(shí)很大,也可以增加分組的個(gè)數(shù)。例如:CAST(RAND() * 1000 AS BIGINT) tag

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1.簡(jiǎn)介 數(shù)據(jù)存儲(chǔ)有哪些方式?電子表格,紙質(zhì)文件,數(shù)據(jù)庫(kù)。 那么究竟什么是關(guān)系型數(shù)據(jù)庫(kù)? 目前對(duì)數(shù)據(jù)庫(kù)的分類主要是...
    喬震閱讀 1,786評(píng)論 0 2
  • Hive性能優(yōu)化 1.概述繼續(xù)《那些年使用Hive踩過(guò)的坑》一文中的剩余部分,本篇博客贅述了在工作中總結(jié)Hive的...
    Albert陳凱閱讀 1,582評(píng)論 0 8
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,778評(píng)論 18 399
  • 【優(yōu)化】COUNT(1)、COUNT(*)、COUNT(常量)、COUNT(主鍵)、COUNT(ROWID)、CO...
    小麥苗DB寶閱讀 2,726評(píng)論 0 3
  • 幸福是由內(nèi)而外散發(fā)的一種滿足感,能夠持久,沒(méi)有壞處,幸福是萬(wàn)能藥。 幸福和快樂(lè)有差異,幸福無(wú)法度量,快樂(lè)短暫,可度...
    happness321閱讀 365評(píng)論 1 3