問題描述
使用Spark寫複雜SQL時,我們經常會遇到兩個尷尬的情況:
表裏的單個文件都很大,而且因爲record比較小,所以單個文件的record數量巨大。
表裏每個分區都有成百上千個小文件,單個文件可能只有幾條記錄
對於第一種情況,會導致我們沒辦法充分利用我們已有的核,所以速度很慢。
對於第二種情況,則會大量浪費調度時間。比如你有100萬個文件,假設只有100個核,那麼需要調度一萬輪,每輪調度除了調度自身的消耗,還有很多額外的消耗。在沒有shuffle的情況下,會導致寫太多文件,本身就浪費性能,如果有shuffle,則對這麼多map進行合併,本身也帶來額外消耗。所以最佳辦法是在讀取的時候就能生成較少的分區,從而減少開銷。
大文件優化讀取優化
大文件優化讀取需要關注兩個參數:
spark.files.maxPartitionBytes= 默認128m
parquet.block.size= 默認128m
第一個參數是針對session有效的,也就是因爲這你在讀的時候設置就會立刻生效。在沒有第二個參數配合的情況下,就已經能夠增加分區數了,缺點是,分區裏的數據可能不會很均勻,因爲均勻程度也會受到第二個參數的影響。
然而,第二個參數需要寫入的時候就配置上。所以大家有條件可以設置下,方便遇到文件較大,然後單條記錄又很小的情況下,查詢可以更靈活控制。
小文件讀取優化
小文件讀取首先有基礎參數要設置(其中parquet已經是默認打開的),分別是針對parquet格式和ORC的。
spark.sql.hive.convertMetastoreParquet=true (默認已經爲true)
spark.sql.hive.convertMetastoreOrc=true
只要是 Parquet 或者 ORC,無論是 Hive 表還是 datasource,默認都走 FileScan,都支持合併。所以大部分情況,這兩個參數大家可以當做不存在。
接下來就是通過另外兩個參數來控制怎麼進行小文件合併了(多個文件合併成一個分區):
spark.files.maxPartitionBytes= 默認128m
spark.files.openCostInBytes= 默認4m
我們簡單解釋下這兩個參數(注意他們的單位都是bytes):
maxPartitionBytes參數控制一個分區最大多少。
openCostInBytes控制當一個文件小於該閾值時,會繼續掃描新的文件將其放到到一個分區
所以理論上你可以將openCostInBytes再設置大一點,減少分區讀取。