有時候會發現即使是讀取少量的資料,啟動延時可能也非常大,針對該現象進行分析,並提供一些解決思路。spark 一次查詢過程可以簡單抽象為 planning 階段和 execution 階段,在乙個新的 spark session 中第一次查詢某資料的過程稱為冷啟動,在這種情況下 planning 的耗時可能會比 execution 更長。
spark 讀取資料冷啟動時,會從檔案系統中獲取檔案的一些元資料資訊(location,size,etc.)用於優化,如果乙個目錄下的檔案過多,就會比較耗時(可能達到數十分鐘),該邏輯在 inmemoryfieindex 中實現。
後續再次多次查詢則會在 filestatuscache 中進行查詢,planning 階段效能也就大幅提公升了,下文將** planning 階段如何載入元資料以及可能的一些優化點。
spark 2.1 版本前,spark 直接從檔案系統中查詢資料的元資料並將其快取到記憶體中,元資料報括乙個 partition 的列表和檔案的一些統計資訊(路徑,檔案大小,是否為目錄,備份數,塊大小,定義時間,訪問時間,資料塊位置資訊)。一旦資料快取後,在後續的查詢中,表的 partition 就可以在記憶體中進行下推,得以快速的查詢。
將元資料快取在記憶體中雖然提供了很好的效能,但也存在2個缺點:在 spark 載入所有表分割槽的元資料之前,會阻塞查詢。對於大型分割槽表,遞迴的掃瞄檔案系統以發現初始查詢檔案的元資料可能會花費數分鐘,特別是當資料儲存在雲端。其次,表的所有元資料都需要放入記憶體中,增加了記憶體壓力。
spark 2.1 針對上述缺點進行了優化,可參考 spark-17861
舊表可以使用msck repair table
命令進行轉化,檢視是否生效,如果partition provider
為catalog
則表示會從 catalog 中獲取分割槽資訊
sql("describe formatted test_table")
.filter("col_name like '%partition provider%'").show
+-------------------+---------+-------+
| col_name|data_type|comment|
+-------------------+---------+-------+
|partition provider:| catalog| |
+-------------------+---------+-------+
效能對比出自官方blog,通過讀取一張表不同的分割槽數,觀察任務 execution time 和 planning time,在spark2.1之前 planning 階段的耗時是相同的,意味著讀取乙個分割槽也需要掃瞄全表的 file status。
雖然優化了避免載入過多元資料的問題,但是單個分割槽下檔案過多導致讀取檔案元資料緩慢的問題並沒有解決。
在 spark-27801 中(將在 spark3.0 release),對乙個目錄下多檔案的場景進行了優化,效能有大幅度的提公升。
使用distributedfilesystem.listlocatedstatus
代替了fs.liststatus
+getfileblocklocations
的方式
listlocatedstatus
// 對 namenode 只發起一次 listlocatedstatus 請求,在方法內部獲得每個檔案 block location 資訊
val statuses = fs.listlocatedstatus(path)
new iterator[locatedfilestatus]() .toarray
statuses.flatmap
fs.liststatus + getfileblocklocations (只展示核心**)
val statuses = fs.liststatus(path)
statuses.flatmap else
}val lfs = new locatedfilestatus(f.getlen, f.isdirectory, f.getreplication, f.getblocksize,
f.getmodificationtime, 0, null, null, null, null, f.getpath, locations)
if (f.issymlink)
some(lfs)
}
效能對比實測乙個57個分割槽,每個分割槽1445個檔案的任務,效能提公升6倍左右
打入 spark-27801 前
打入 spark-27801 後
讀取資料時會先判斷分割槽的數量,如果分割槽數量小於等於spark.sql.sources.parallelpartitiondiscovery.threshold (預設32)
,則使用 driver 迴圈讀取檔案元資料,如果分割槽數量大於該值,則會啟動乙個 spark job,分布式的處理元資料資訊(每個分割槽下的檔案使用乙個task進行處理)
分割槽數量很多意味著 listing leaf files task 的任務會很多,分割槽裡的檔案數量多意味著每個 task 的負載高,使用 filestatuscache 快取檔案狀態,預設的快取spark.sql.hive.filesourcepartitionfilecachesize
為 250mb
tip
listing leaf files task 的數量計算公式為
val numparallelism = math.min(paths.size, parallelpartitiondiscoveryparallelism)
其中,paths.size
為需要讀取的分割槽數量,parallelpartitiondiscoveryparallelism
由引數spark.sql.sources.parallelpartitiondiscovery.parallelism
控制,預設為10000,目的是防止 task 過多,但從生產任務上觀察發現大多數 get status task 完成的時間都是毫秒級,可以考慮把這個值調低,減少任務啟動關閉的開銷,或者直接修改原始碼將paths.size
按一定比例調低,例如paths.size/2
控制 task 數量之前
控制 task 數量之後
spark 查詢冷啟動(獲取檔案元資料效能)對比前幾個版本已經有非常大提公升,降低了查詢的延時
一些思考,是否可以考慮用redis
替換filestatuscache
,在資料寫入的時候更新 redis 中的 file status 資訊,這樣就相當於所有的 spark 應用共享了filestatuscache
,減少了記憶體使用的同時也不再有讀資料冷啟動的問題了。
Android效能優化之冷啟動優化
冷啟動 cold start 溫啟動 warm start 熱啟動 hot start adb shell am start w packagename packagename.activity 例如 adb shell am start w com.dateyou.test com.datayou...
Android異常與效能優化 冷啟動優化
冷啟動優化 一 什麼是冷啟動 1.冷啟動的定義 冷啟動就是在啟動應用前,系統中沒有該應用的任何程序資訊 2.冷啟動 熱啟動的區別 熱啟動 使用者使用返回鍵退出應用,然後馬上又重新啟動應用 特點3.冷啟動時間計算 這個時間值從應用啟動 建立程序 開始計算,到完成檢視的第一次繪製 即activity內容...
Android 冷啟動(所謂白屏)優化方案
在styles.xml中自定義乙個style stylename welcometheme parent itemname android windowbackground drawable welcomelaunch item 這裡是設定你想要顯示的 itemname android window...