咱們知道,大部分Spark計算都是在內存中完成的,因此Spark的瓶頸通常來自於集羣(standalone, yarn, mesos, k8s)的資源緊張,CPU,網絡帶寬,內存。Spark的性能,想要它快,就得充分利用好系統資源,尤爲是內存和CPU。有時候咱們也須要作一些優化調整來減小內存佔用,例如將小文件進行合併的操做。segmentfault
咱們有一個15萬條總數據量133MB的表,使用SELECT * FROM bi.dwd_tbl_conf_info全表查詢耗時3min,另一個500萬條總數據量6.3G的表ods_tbl_conf_detail,查詢耗時23秒。兩張表均爲列式存儲的表。網絡
大表查詢快,而小表反而查詢慢了,爲何會產生如此奇怪的現象呢?性能
數據量6.3G的表查詢耗時23秒,反而數據量133MB的小表查詢耗時3min,這很是奇怪。咱們收集了對應的建表語句,發現二者沒有太大的差別,大部分爲String,兩表的列數也相差不大。優化
CREATE TABLE IF NOT EXISTS `bi`.`dwd_tbl_conf_info` ( `corp_id` STRING COMMENT '', `dept_uuid` STRING COMMENT '', `user_id` STRING COMMENT '', `user_name` STRING COMMENT '', `uuid` STRING COMMENT '', `dtime` DATE COMMENT '', `slice_number` INT COMMENT '', `attendee_count` INT COMMENT '', `mr_id` STRING COMMENT '', `mr_pkg_id` STRING COMMENT '', `mr_parties` INT COMMENT '', `is_mr` TINYINT COMMENT 'R', `is_live_conf` TINYINT COMMENT '' )
CREATE TABLE IF NOT EXISTS `bi`.`ods_tbl_conf_detail` ( `id` string, `conf_uuid` string, `conf_id` string, `name` string, `number` string, `device_type` string, `j_time` bigint, `l_time` bigint, `media_type` string, `dept_name` string, `UPDATETIME` bigint, `CREATETIME` bigint, `user_id` string, `USERAGENT` string, `corp_id` string, `account` string )
由於兩張表均爲很簡單的SELECT查詢操做,無任何複雜的聚合join操做,也無UDF相關的操做,因此基本確認查詢慢的應該發生的讀表的時候,咱們將懷疑的點放到了讀表操做上。經過查詢兩個查詢語句的DAG和任務分佈,咱們發現了不同的地方。ui
查詢快的表,查詢時總共有68個任務,任務分配好比均勻,平均7~9s左右,而查詢慢的表,查詢時總共1160個任務,平均也是9s左右。以下圖所示:spa
至此,咱們基本發現了貓膩所在。大表6.3G但文件個數小,只有68個,因此很快跑完了。而小表雖然只有133MB,但文件個數特別多,致使產生的任務特別多,而因爲單個任務自己比較快,大部分時間花費在任務調度上,致使任務耗時較長。code
那如何才能解決小表查詢慢的問題呢?blog
那如今擺在咱們面前就存在如今問題:內存
一、爲何小表會產生這麼小文件資源
二、已經產生的這麼小文件如何合併
帶着這兩個問題,咱們和業務的開發人員聊了一個發現小表是業務開發人員從原始數據表中,按照不一樣的時間切片查詢並作數據清洗後插入到小表中的,而因爲時間切片切的比較小,致使這樣的插入次數特別多,從而產生了大量的小文件。
那麼咱們須要解決的問題就是2個,如何才能把這些歷史的小文件進行合併以及如何才能保證後續的業務流程中再也不產生小文件,咱們指導業務開發人員作了如下優化:
1)使用INSERT OVERWRITE bi.dwd_tbl_conf_info SELECT * FROM bi.dwd_tbl_conf_info合併下歷史的數據。因爲DLI作了數據一致性保護,OVERWRITE期間不影響原有數據的讀取和查詢,OVERWRITE以後就會使用新的合併後的數據。合併後全表查詢由原來的3min縮短到9s內完成。
2)原有表修改成分區表,插入時不一樣時間放入到不一樣分區,查詢時只查詢須要的時間段內的分區數據,進一步減少讀取數據量。