今天將臨時表里面的數據按照天分區插入到線上的表中去,出現了Hive創建的文件數大於100000個的情況,我的SQL如下:
hive> insert overwrite table test partition(dt) > select * from iteblog_tmp; |
iteblog_tmp表里面一共有570多G的數據,一共可以分成76個分區,SQL運行的時候創建了2163個Mapper,0個Reducers。程序運行到一般左右的時候出現了以下的異常:
[Fatal Error] total number of created files now is 100385, which exceeds 100000. Killing the job. |
並最終導致了SQL的運行失敗。這個錯誤的原因是因為Hive對創建文件的總數有限制(hive.exec.max.created.files),默認是100000個,而這個SQL在運行的時候每個Map都會創建76個文件,對應了每個分區,所以這個SQL總共會創建2163 * 76 = 164388個文件,運行中肯定會出現上述的異常。為了能夠成功地運行上述的SQL,最簡單的方法就是加大hive.exec.max.created.files參數的設置。但是這有個問題,這會導致在iteblog中產生大量的小文件,因為iteblog_tmp表的數據就570多G,那么平均每個文件的大小=570多G / 164388 = 3.550624133148405MB,可想而知,十萬多個這么小的小文件對Hadoop來說是多么不好。那么有沒有好的辦法呢?有!
我們可以將dt相同的數據放到同一個Reduce處理,這樣最多也就產生76個文件,將dt相同的數據放到同一個Reduce可以使用DISTRIBUTE BY dt實現,所以修改之后的SQL如下:
hive> insert overwrite table test partition(dt) > select * from iteblog_tmp > DISTRIBUTE BY dt; |
修改完之后的SQL運行良好,並沒有出現上面的異常信息,但是這里也有個問題,因為這76個分區的數據分布很不均勻,有些Reduce的數據有30多G,而有些Reduce只有幾K,直接導致了這個SQL運行的速度很慢!
能不能將570G的數據均勻的分配給Reduce呢?可以!我們可以使用DISTRIBUTE BY rand()將數據隨機分配給Reduce,這樣可以使得每個Reduce處理的數據大體一致。我設定每個Reduce處理5G的數據,對於570G的數據總共會起110左右的Reduces,修改的SQL如下:
hive> set hive.exec.reducers.bytes.per.reducer=5120000000; hive> insert overwrite table test partition(dt) > select * from iteblog_tmp > DISTRIBUTE BY rand(); |
這個SQL運行的時間很不錯,而且生產的文件數量為Reduce的個數*分區的個數,不到1W個文件。