Hive去重統計
相信使用Hive的人平時會經常用到去重統計之類的吧,但是好像平時很少關注這個去重的性能問題,但是當一個表的數據量非常大的時候,會發現一個簡單的count(distinct order_no)
這種語句跑的特別慢,和直接運行count(order_no)
的時間差了很多,於是研究了一下。
先說結論:能使用group by
代替distinc
就不要使用distinct
,例子:
實際論證
order_snap為訂單的快照表 總記錄條數763191489,即將近8億條記錄,總大小:108.877GB,存儲的是公司所有的訂單信息,表的字段大概有20個,其中訂單號是沒有重復的,所以在統計總共有多少訂單號的時候去重不去重結果都一樣,我們來看看:
統計所有的訂單有多少條條數,一個count
函數就可以搞定的sql性能如何。
- DISTINCT
1
2
3
4
5
6
7
|
select count(distinct order_no) from order_snap;
Stage-Stage-1: Map:
396 Reduce: 1 Cumulative CPU: 7915.67 sec HDFS Read: 119072894175 HDFS Write: 10 SUCCESS
Total MapReduce CPU Time Spent:
0 days 2 hours 11 minutes 55 seconds 670 msec
OK
_c0
763191489
Time taken: 1818.864 seconds, Fetched:
1 row(s)
|
- GROUP BY
1
2
3
4
5
6
7
8
|
select count(t.order_no) from (select order_no from order_snap group by order_no) t;
Stage-Stage-1: Map:
396 Reduce: 457 Cumulative CPU: 10056.7 sec HDFS Read: 119074266583 HDFS Write: 53469 SUCCESS
Stage-Stage-2: Map:
177 Reduce: 1 Cumulative CPU: 280.22 sec HDFS Read: 472596 HDFS Write: 10 SUCCESS
Total MapReduce CPU Time Spent:
0 days 2 hours 52 minutes 16 seconds 920 msec
OK
_c0
763191489
Time taken: 244.192 seconds, Fetched:
1 row(s)
|
結論:第二種寫法的性能是第一種的7.448499541
倍
注意到為什么會有這個差異,Hadoop其實就是處理大數據的,Hive並不怕數據有多大,怕的就是數據傾斜,我們看看兩者的輸出信息:
1
2
3
4
|
# distinct
Stage-Stage-1: Map:
396 Reduce: 1 Cumulative CPU: 7915.67 sec HDFS Read: 119072894175 HDFS Write: 10 SUCCESS
# group by
Stage-Stage-1: Map:
396 Reduce: 457 Cumulative CPU: 10056.7 sec HDFS Read: 119074266583 HDFS Write: 53469 SUCCESS
|
發現貓膩了沒有,使用distinct會將所有的order_no都shuffle到一個reducer里面,這就是我們所說的數據傾斜,都傾斜到一個reducer這樣性能能不低么?再看第二個,直接按訂單號分組,起了457個reducer
,將數據分布到多台機器上執行,時間當然快了.
由於沒有手動指定Reduce的個數,Hive會根據數據的大小動態的指定Reduce大小,你也可以手動指定
1
|
hive> set mapred
.reduce.tasks=100;
|
類似這樣,所以如果數據量特別大的情況下,盡量不要使用distinct
吧。
但是如果你想在一條語句里看總記錄條數以及去重之后的記錄條數,那沒有辦法過濾,所以你有兩個選擇,要么使用兩個sql語句分別跑,然后union all或者就使用普通的distinct。具體來說得看具體情況,直接使用distinct可讀性好,數據量如果不大的話推薦使用,如果數據太大了,性能受到影響了,再考慮優化